From patchwork Fri Nov 17 14:50:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Coplan X-Patchwork-Id: 80134 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AB7963857001 for ; Fri, 17 Nov 2023 14:52:17 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from EUR04-HE1-obe.outbound.protection.outlook.com (mail-he1eur04on2089.outbound.protection.outlook.com [40.107.7.89]) by sourceware.org (Postfix) with ESMTPS id 11D9E3858D20 for ; Fri, 17 Nov 2023 14:51:50 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 11D9E3858D20 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 11D9E3858D20 Authentication-Results: server2.sourceware.org; arc=pass smtp.remote-ip=40.107.7.89 ARC-Seal: i=3; a=rsa-sha256; d=sourceware.org; s=key; t=1700232715; cv=pass; b=YGq9JcVxD+9GrqLAAQ2BmlyEJoWIny6Pcu1Z6qM7jWQSu3ET4HNMo8fiElnuwp+r64UfIbWR30gVn55HIOJ4S95pH6Q0c68pSn2r00payoXtvPUDS2u795+EZRCTCLJBsj3TJPKWnRY3JfMFCKjmFWlpMz7H1B7e9DJrC5RZotU= ARC-Message-Signature: i=3; a=rsa-sha256; d=sourceware.org; s=key; t=1700232715; c=relaxed/simple; bh=nuIrpo4eAiZu8ODs/l4fR0B/57H5kcG2Rij+KtIwP/Q=; h=DKIM-Signature:DKIM-Signature:Date:From:To:Subject:Message-ID: MIME-Version; b=o5VUvOscCMFonB/LsEFLWiE+gJhfXx1+vKkpDnkmZg3HVU9D0053hGZy/QcZBxbc0dJjex38J8fI+Fg+FVRS2fGwatOMiutMgD4nysuACil0kD3dduaGYYX4cnW10g0W1URoYoj7RnWhg25DETV6nSOFB0+RQ+9kxVlWDMFy8h4= ARC-Authentication-Results: i=3; server2.sourceware.org ARC-Seal: i=2; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=pass; b=FDXfDSsdYtw8Yjm4DyHICuznNYe9RIToIq+d78Ut0r6N+bpnQwcU6ZF/nR0PqrsajDLXNbv0cPKj2v/yJKk6DMpYPMLDSGU68BJ+fUOmB6eNV6f+PBW4d6RDwS1UumZGOi1HJYYlQ2z23HCFmiY69pXgNNzZ0TZlpdqMO0PPs48nrVHq+qVDji6SLSDDa+ONQwDokezjTMQK54xRq0AHbckKQZzJe8Kdf0DSSXNieUpRtfQjL0FpiEckwQYHTE6W1l1YmCni0WaT+tYcS16jsmVpGrx2jRH/8Ju8BBUBWb7CLu4meUI+74ICfhMkbt+pjqScV1VTkGexeYFdkfrLmw== ARC-Message-Signature: i=2; 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=6UK4IU1ECrXCn65y5kD1IfD1OaW4m9FGfQbX6xNO9XY=; b=SnzkxLyTiwEr7rOpbSHECH4YXAEb7qZjlI0xrJTfWqgY1sI9sXEtw2U+DhpCL8tQNyBqUVDH0fXNhvcZnZhGxTeqMdCdu2vfkDVUg2yRrpQFCZZmJI4Pv8bi2aM0Hvdz4eqdJ6F3Xz4HimZ+dAX3pVwC4OUjPNt9wY6WlRlhscT4f9DIckc2YshpTvXZBY2bU6L8PewK9+ffQa64mjeKw3Gnezw9Lf+qsNdVPxc+8BT9qG7yEwTG9mUdbnwluaoul+85Ck3H1hsm1qIsmZP+wfF3KtjxC0l9Plbo/0FuWRk+xfSosiu8fIH0E0QIhfBNTtcs5zk/qCtpfnHg0Wga+g== ARC-Authentication-Results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 63.35.35.123) smtp.rcpttodomain=gcc.gnu.org smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com; arc=pass (0 oda=1 ltdi=1 spf=[1,1,smtp.mailfrom=arm.com] dkim=[1,1,header.d=arm.com] dmarc=[1,1,header.from=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=6UK4IU1ECrXCn65y5kD1IfD1OaW4m9FGfQbX6xNO9XY=; b=wbq9JIL0ydN3aL/QrQ8qyrPaHVYehf2DUoJZP3zrTJQ4+jYXJY2XzYfezH2CUL47cyzhjb4QoJ0d5oJlNWeUdP15FBSHusUuxydqLent17jrA0mdtRkUFxvIrMV64L9M9t5UXcsg/tBMoo7ialFft2oPFRyn/q34TR4zrareuXA= Received: from DUZPR01CA0217.eurprd01.prod.exchangelabs.com (2603:10a6:10:4b4::11) by AS1PR08MB7636.eurprd08.prod.outlook.com (2603:10a6:20b:478::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7002.23; Fri, 17 Nov 2023 14:51:46 +0000 Received: from DB5PEPF00014B9F.eurprd02.prod.outlook.com (2603:10a6:10:4b4:cafe::9d) by DUZPR01CA0217.outlook.office365.com (2603:10a6:10:4b4::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7002.23 via Frontend Transport; Fri, 17 Nov 2023 14:51:46 +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 DB5PEPF00014B9F.mail.protection.outlook.com (10.167.8.169) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7025.12 via Frontend Transport; Fri, 17 Nov 2023 14:51:46 +0000 Received: ("Tessian outbound 385ad2f98d71:v228"); Fri, 17 Nov 2023 14:51:46 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: 0e7b8b2bcfbf17e3 X-CR-MTA-TID: 64aa7808 Received: from ea5a2e1a410c.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 08FC102A-3E96-4DE4-8CC5-0DF63197293D.1; Fri, 17 Nov 2023 14:50:57 +0000 Received: from EUR05-AM6-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id ea5a2e1a410c.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Fri, 17 Nov 2023 14:50:57 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=oDyn6alDv1yLfn/SjJN4UxW0x/LoIMC1QdjkVDk+brEYZ8jb6w9EBKNDcWsaGW4Y++qQlU3h04SUX8YdQwPLD3eKuhxfuf3Jwd/iS+ZWjtXP0YN8GbLZ62iUsIg8bmUQMtI8R/TeZYaeJ0haJ27qu3LlXoupPon6umfczrMvRN3yVhYzoO2X+DiEM6pLJVBT6KLOPkkngdec2rkmF/Guw8Mdux84wzRRuU7ePIuRz38ZfIFhAy/mKfCNMvHuAoza+tuYoAw7pcQlO95EQxAJbSeDKL69WkBo06cWE1InEeoUwCBj+LZNvUgEVvGNfoiFBimgRmuUZmkscxWkGHtWhw== 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=6UK4IU1ECrXCn65y5kD1IfD1OaW4m9FGfQbX6xNO9XY=; b=jeZzIZGPM8/szeiOEj0+h6i89VkZZml0UysAy1Sn0Ad9upZjemVgyiH46olMvK/nzTSaRYLfMN7YNo/U9+Oy3o76OZoYJkLTch8LVRAGA4J8IJYo9SZfSVzjZk+35c3+eL7Yk0Y3pUE0jAImW0IiJGw3M0NMQi7eNsZf9qkmoAouqKin35kbk6z+fKr5oY3OkfIVhW1gQw4ApTgyNPKFIX5LBOOg3ShJGw1MwKU5RyBE6kqwERl7+VLUZsq7cUwZpXqoHA8Vgq1KtNshN19Wqg0Ih1I+wP+KVyp2sRMXiRSt88BOSFReNxIVXq2ROMyTyxFllpMEHDGQ+qFGuMqYSQ== 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=6UK4IU1ECrXCn65y5kD1IfD1OaW4m9FGfQbX6xNO9XY=; b=wbq9JIL0ydN3aL/QrQ8qyrPaHVYehf2DUoJZP3zrTJQ4+jYXJY2XzYfezH2CUL47cyzhjb4QoJ0d5oJlNWeUdP15FBSHusUuxydqLent17jrA0mdtRkUFxvIrMV64L9M9t5UXcsg/tBMoo7ialFft2oPFRyn/q34TR4zrareuXA= 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 DB9PR08MB6617.eurprd08.prod.outlook.com (2603:10a6:10:261::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7002.23; Fri, 17 Nov 2023 14:50:52 +0000 Received: from PAWPR08MB8958.eurprd08.prod.outlook.com ([fe80::8512:cc10:24d4:1919]) by PAWPR08MB8958.eurprd08.prod.outlook.com ([fe80::8512:cc10:24d4:1919%5]) with mapi id 15.20.7002.022; Fri, 17 Nov 2023 14:50:52 +0000 Date: Fri, 17 Nov 2023 14:50:45 +0000 From: Alex Coplan To: gcc-patches@gcc.gnu.org Cc: Marek Polacek , Jason Merrill , Nathan Sidwell , Joseph Myers , Iain Sandoe Subject: [PATCH v5] c-family: Implement __has_feature and __has_extension [PR60512] Message-ID: Content-Disposition: inline X-ClientProxiedBy: LO4P123CA0301.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:196::18) To PAWPR08MB8958.eurprd08.prod.outlook.com (2603:10a6:102:33e::15) MIME-Version: 1.0 X-MS-TrafficTypeDiagnostic: PAWPR08MB8958:EE_|DB9PR08MB6617:EE_|DB5PEPF00014B9F:EE_|AS1PR08MB7636:EE_ X-MS-Office365-Filtering-Correlation-Id: 1fee33b4-3bfb-4772-f243-08dbe77cb8ed 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: QMA3qF5r7o/Gb0B45WjrSiwNl3RCAGylx1VyNsOfIco8Dt0OENhHHRViY4dZUpuW2MoyZMT7X7Ys+FHFTQsOMw4AWXAXMhK8qJwWewP18ejsdYc7DO6Hq2ke3suk5BiJTu3nW0fuAzUlxU55k6CNg5ncTZ52tc3zc+4dGJ3Uf+TJbLIjXpAinxWEeg1XQiJSag1cvmzzjR/oCtveTqkSLU3MH0B/vqaCN8PlnQ+AfXBmBz+dKuD5shG6HrDv7SnztmlMpBosXtUCBw+CnxospGu6rLeQXrnekyk0hmLVloSlyuYX4SPegeVKxXcYh8Dr5Tgc9m135SVc/nmObWZ0JrOpNBFe6JuGwh3AdbN8d/ihl/y3Eu6jrpSK0ThXDngE3QZeyDOY1xQe8a0axz1RKQH7DG3NizhqGyhuYssukTu0WcvApN68eLtCYCtrKsDB330FIKfYGSzXkyw6CE9Fs044kMWC+eqDpc3pJePZzQMWCdmxlXLx30iKN3Dllm1jTNbO2hsn6vvKs0Mcfu/WCI/UIOYPRByXh+XP7NL1sZwSd6Ut5VvvHkUL+YG9CLMg7cZIDPlEP5o4xkeZVcPAywLI1WyGj5ciGV2VAtF7XiKbwVIQpKpQIz5tttiI8Ep/RKY6reQuL0DrgW/ThQ3HnA== 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:(13230031)(39860400002)(366004)(346002)(376002)(136003)(396003)(230922051799003)(451199024)(1800799009)(186009)(64100799003)(966005)(36756003)(6512007)(6486002)(316002)(6916009)(478600001)(38100700002)(21480400003)(41300700001)(8936002)(84970400001)(8676002)(5660300002)(4326008)(235185007)(44832011)(33964004)(44144004)(6506007)(26005)(6666004)(86362001)(66556008)(66946007)(66476007)(2906002)(2616005)(54906003)(2700100001); DIR:OUT; SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR08MB6617 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: DB5PEPF00014B9F.eurprd02.prod.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: 2d906ae9-a79a-411d-8277-08dbe77c9679 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: cL6Cs7LYVRUc6neexZx1ZOGkah25xL7ObvvMQsanwFM06OEEf55zEcQJHONcy7gOwaHiIvC4nLtn9cE62TjgXM0Y04iMglfKwjPe6rH8+RBcyh/WmaVmMMWWes6dUSUntm0hBEsnRidGMjoNFcCdzvPen32AjNtDe8GDKkIlLakPIiZXRnKrhCGTPo/E6PejN6QJXMJFTb4iUqjfRTchVU7AjCiezJEWhJfC3EB23aBfc0BzF1bHieqK/MmpeGOORPE/oenz1CGFyCABZg7jp15eNERrwTSAMIMmCAXlChrVrPJETEq4zCXWAQpoXTTjlIPoigd53+E0kv14UHyHnr0COLjqs9j8/7Ibmm0kKUbTz2QzDz3c2biR7nbtmDbOjK/xemM/qa8R8ifxkVvHvqUuuDy2RvUcNjXVfriz6OTIEhTT7J1znY8Ouj3mfFEQ4METmDaG/LoFrjzW1mVgE8KQ93V9u0QvMYzrVZDolMswOOMvf40PCo9iSaPy0jkZORxFTTDtiYlhU5HUCfy+pbPFY/S6p2VdVc1SSSiKxY8J9N+f2SziT7JII9jgIwwKqFknDYRRbGtHFyrFICPQr/3vR+G4Wf/US6eZLhina+sws0sjy5foogCV+sazhYuh6MTCKLpqVH8/78qMHujENhvZ49Jr8HQaiYDemPrfh1Gqs2bZOTUCnOJag/qZCFs/n8bHDkpr5zKx01RnQh4KDCTgbpZ+DcbEzf3M/pWcTzzK1uWShPb7/nTAvr5ASSHitAtVKf76Rwqat3paBN3OX2j24bDAfg8Rrq+hHnXdsUY= 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:(13230031)(4636009)(376002)(346002)(396003)(39860400002)(136003)(230922051799003)(82310400011)(64100799003)(451199024)(186009)(1800799009)(36840700001)(46966006)(40470700004)(8936002)(8676002)(107886003)(6512007)(26005)(84970400001)(70586007)(70206006)(4326008)(5660300002)(6666004)(235185007)(478600001)(2906002)(6486002)(966005)(41300700001)(33964004)(44144004)(2616005)(6506007)(336012)(21480400003)(47076005)(40480700001)(6916009)(54906003)(316002)(44832011)(36860700001)(82740400003)(356005)(81166007)(40460700003)(86362001)(36756003)(2700100001); DIR:OUT; SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Nov 2023 14:51:46.7619 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 1fee33b4-3bfb-4772-f243-08dbe77cb8ed 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: DB5PEPF00014B9F.eurprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS1PR08MB7636 X-Spam-Status: No, score=-12.0 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Hi, This is a v5 patch to address Marek's feedback here: https://gcc.gnu.org/pipermail/gcc-patches/2023-November/635157.html I also implemented Jason's suggestion to use constexpr for the tables from this review: https://gcc.gnu.org/pipermail/gcc-patches/2023-October/634484.html I'll attach the incremental change in reply to Marek's review to make things easier to compare. Bootstrapped/regtested on aarch64-linux-gnu. Bootstrap/regtest on x86_64-apple-darwin in progress (on top of this libsanitizer fix: https://github.com/llvm/llvm-project/issues/72639). OK for trunk if testing passes? Thanks, Alex -- >8 -- This patch implements clang's __has_feature and __has_extension in GCC. Currently the patch aims to implement all documented features (and some undocumented ones) following the documentation at https://clang.llvm.org/docs/LanguageExtensions.html with the exception of the legacy features for C++ type traits. These are omitted, since as the clang documentation notes, __has_builtin is the correct "modern" way to query for these (which GCC already implements). gcc/c-family/ChangeLog: PR c++/60512 * c-common.cc (struct hf_feature_info): New. (c_common_register_feature): New. (init_has_feature): New. (has_feature_p): New. * c-common.h (c_common_has_feature): New. (c_family_register_lang_features): New. (c_common_register_feature): New. (has_feature_p): 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 (c_family_register_lang_features): New. * c-objc-common.cc (struct c_feature_info): New. (c_register_features): New. gcc/cp/ChangeLog: PR c++/60512 * cp-lang.cc (c_family_register_lang_features): New. * cp-objcp-common.cc (struct cp_feature_selector): New. (cp_feature_selector::has_feature): New. (struct cp_feature_info): New. (cp_register_features): New. gcc/ChangeLog: PR c++/60512 * doc/cpp.texi: Document __has_{feature,extension}. gcc/objc/ChangeLog: PR c++/60512 * objc-act.cc (struct objc_feature_info): New. (objc_nonfragile_abi_p): New. (objc_common_register_features): New. * objc-act.h (objc_common_register_features): New. * objc-lang.cc (c_family_register_lang_features): New. gcc/objcp/ChangeLog: PR c++/60512 * objcp-lang.cc (c_family_register_lang_features): New. 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. * c-c++-common/has-feature-pedantic.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 0ea0c4f4bef..f270fa2f5b5 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -311,6 +311,44 @@ const struct fname_var_t fname_vars[] = {NULL, 0, 0}, }; +/* Flags to restrict availability of generic features that + are known to __has_{feature,extension}. */ + +enum +{ + HF_FLAG_NONE = 0, + HF_FLAG_EXT = 1, /* Available only as an extension. */ + HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags. */ +}; + +/* Info for generic features which can be queried through + __has_{feature,extension}. */ + +struct hf_feature_info +{ + const char *ident; + unsigned flags; + unsigned mask; +}; + +/* Table of generic features which can be queried through + __has_{feature,extension}. */ + +static constexpr 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", HF_FLAG_NONE, 0 }, + { "attribute_unavailable_with_message", HF_FLAG_NONE, 0 }, + { "enumerator_attributes", HF_FLAG_NONE, 0 }, + { "tls", HF_FLAG_NONE, 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; @@ -9877,4 +9915,63 @@ c_strict_flex_array_level_of (tree array_field) return strict_flex_array_level; } +/* Map from identifiers to booleans. Value is true for features, and + false for extensions. Used to implement __has_{feature,extension}. */ + +using feature_map_t = hash_map ; +static feature_map_t *feature_map; + +/* Register a feature for __has_{feature,extension}. FEATURE_P is true + if the feature identified by NAME is a feature (as opposed to an + extension). */ + +void +c_common_register_feature (const char *name, bool feature_p) +{ + bool dup = feature_map->put (get_identifier (name), feature_p); + gcc_checking_assert (!dup); +} + +/* Lazily initialize hash table for __has_{feature,extension}, + dispatching to the appropriate front end to register language-specific + features. */ + +static void +init_has_feature () +{ + gcc_checking_assert (!feature_map); + feature_map = new feature_map_t; + + for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++) + { + const hf_feature_info *info = has_feature_table + i; + + if ((info->flags & HF_FLAG_SANITIZE) && !(flag_sanitize & info->mask)) + continue; + + const bool feature_p = !(info->flags & HF_FLAG_EXT); + c_common_register_feature (info->ident, feature_p); + } + + /* Register language-specific features. */ + c_family_register_lang_features (); +} + +/* If STRICT_P is true, evaluate __has_feature (IDENT). + Otherwise, evaluate __has_extension (IDENT). */ + +bool +has_feature_p (const char *ident, bool strict_p) +{ + if (!feature_map) + init_has_feature (); + + tree name = canonicalize_attr_name (get_identifier (ident)); + bool *feat_p = feature_map->get (name); + if (!feat_p) + return false; + + return !strict_p || *feat_p; +} + #include "gt-c-family-c-common.h" diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index b57e83d7c5d..a5359b7caa5 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1126,6 +1126,20 @@ 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); + +/* Implemented by each front end in *-lang.cc. */ +extern void c_family_register_lang_features (); + +/* Implemented in c-family/c-common.cc. */ +extern void c_common_register_feature (const char *, bool); +extern bool has_feature_p (const char *, bool); + +/* Implemented in c/c-objc-common.cc. */ +extern void c_register_features (); + +/* Implemented in cp/cp-objcp-common.cc. */ +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 06c2453c89a..e837c7459d5 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; @@ -451,16 +452,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; } @@ -480,7 +481,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; } @@ -499,9 +500,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 ddfd3e80074..ff6633e4701 100644 --- a/gcc/c/c-lang.cc +++ b/gcc/c/c-lang.cc @@ -63,6 +63,15 @@ c_get_sarif_source_language (const char *) return "c"; } +/* Implement c-family hook to register language-specific features for + __has_{feature,extension}. */ + +void +c_family_register_lang_features () +{ + c_register_features (); +} + #if CHECKING_P namespace selftest { diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc index c8f49aa2370..53eda7fa707 100644 --- a/gcc/c/c-objc-common.cc +++ b/gcc/c/c-objc-common.cc @@ -34,6 +34,39 @@ 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 **); +/* Info for C language features which can be queried through + __has_{feature,extension}. */ + +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 }, + { "cxx_binary_literals", &flag_isoc23 } +}; + +/* Register features specific to the C language. */ + +void +c_register_features () +{ + for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++) + { + const c_feature_info *info = c_feature_table + i; + const bool feat_p = !info->enable_flag || *info->enable_flag; + c_common_register_feature (info->ident, feat_p); + } +} + bool c_missing_noreturn_ok_p (tree decl) { diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc index f2ed83de4fb..bdc25ddd9ff 100644 --- a/gcc/cp/cp-lang.cc +++ b/gcc/cp/cp-lang.cc @@ -119,6 +119,15 @@ objcp_tsubst_expr (tree /*t*/, tree /*args*/, tsubst_flags_t /*complain*/, return NULL_TREE; } +/* Implement c-family hook to add language-specific features + for __has_{feature,extension}. */ + +void +c_family_register_lang_features () +{ + cp_register_features (); +} + static const char * cxx_dwarf_name (tree t, int verbosity) { diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc index 2093ae02466..70f9e4ab9e4 100644 --- a/gcc/cp/cp-objcp-common.cc +++ b/gcc/cp/cp-objcp-common.cc @@ -23,10 +23,154 @@ 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" +/* Class to determine whether a given C++ language feature is available. + Used to implement __has_{feature,extension}. */ + +struct cp_feature_selector +{ + enum + { + DIALECT, + FLAG + } kind; + + enum class result + { + NONE, + EXT, + FEAT + }; + + 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) {} + + inline result has_feature () const; +}; + +/* Check whether this language feature is available as a feature, + extension, or not at all. */ + +cp_feature_selector::result +cp_feature_selector::has_feature () const +{ + switch (kind) + { + case DIALECT: + if (cxx_dialect >= dialect.feat) + return result::FEAT; + else if (cxx_dialect >= dialect.ext) + return result::EXT; + else + return result::NONE; + case FLAG: + return *enable_flag ? result::FEAT : result::NONE; + } + + gcc_unreachable (); +} + +/* Information about a C++ language feature which can be queried + through __has_{feature,extension}. IDENT is the name of the feature, + and SELECTOR encodes how to compute whether the feature is available. */ + +struct cp_feature_info +{ + const char *ident; + cp_feature_selector selector; +}; + +/* Table of features for __has_{feature,extension}. */ + +static constexpr 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 }, +}; + +/* Register C++ language features for __has_{feature,extension}. */ + +void +cp_register_features () +{ + using result = cp_feature_selector::result; + + for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++) + { + const cp_feature_info *info = cp_feature_table + i; + const auto res = info->selector.has_feature (); + if (res == result::NONE) + continue; + + c_common_register_feature (info->ident, res == result::FEAT); + } +} + /* 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 04f357c74b2..7bfe72b3e4e 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,45 @@ 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. + +Note that @code{__has_feature} and @code{__has_extension} are not recommended +for use in new code, and are only provided for compatibility with Clang. For +details of which identifiers are accepted by these function-like macros, see +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension, +the Clang documentation}}. + +@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 @option{-pedantic-errors} flag is given, @code{__has_extension} is +equivalent to @code{__has_feature}. + +Note that @code{__has_feature} and @code{__has_extension} are not recommended +for use in new code, and are only provided for compatibility with Clang. For +details of which identifiers are accepted by these function-like macros, see +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension, +the Clang documentation}}. + @node @code{__has_include} @subsection @code{__has_include} @cindex @code{__has_include} diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc index fe19aa55277..02ed6d92eb9 100644 --- a/gcc/objc/objc-act.cc +++ b/gcc/objc/objc-act.cc @@ -10340,5 +10340,51 @@ objc_common_init_ts (void) MARK_TS_TYPED (PROPERTY_REF); } +/* Information for Objective-C-specific features known to __has_feature. */ + +struct objc_feature_info +{ + typedef bool (*predicate_t) (); + + const char *ident; + predicate_t predicate; + + constexpr objc_feature_info (const char *name) + : ident (name), predicate (nullptr) {} + constexpr objc_feature_info (const char *name, predicate_t p) + : ident (name), predicate (p) {} + + bool has_feature () const + { + return predicate ? predicate () : true; + } +}; + +static bool objc_nonfragile_abi_p () +{ + return flag_next_runtime && flag_objc_abi >= 2; +} + +static constexpr objc_feature_info objc_features[] = +{ + { "objc_default_synthesize_properties" }, + { "objc_instancetype" }, + { "objc_nonfragile_abi", objc_nonfragile_abi_p } +}; + +/* Register Objective-C-specific features for __has_feature. */ + +void +objc_common_register_features () +{ + for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++) + { + const objc_feature_info *info = objc_features + i; + if (!info->has_feature ()) + continue; + + c_common_register_feature (info->ident, true); + } +} #include "gt-objc-objc-act.h" diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h index 68f90296db3..df40127ab9c 100644 --- a/gcc/objc/objc-act.h +++ b/gcc/objc/objc-act.h @@ -29,6 +29,9 @@ int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *); void objc_common_init_ts (void); const char *objc_get_sarif_source_language (const char *); +/* 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 8e56de6a81c..107fb0e8138 100644 --- a/gcc/objc/objc-lang.cc +++ b/gcc/objc/objc-lang.cc @@ -56,6 +56,16 @@ objc_get_sarif_source_language (const char *) return "objectivec"; } +/* Implement c-family hook to add language-specific features + for __has_{feature,extension}. */ + +void +c_family_register_lang_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 ee39aece848..8a2c9f74250 100644 --- a/gcc/objcp/objcp-lang.cc +++ b/gcc/objcp/objcp-lang.cc @@ -85,6 +85,16 @@ objcp_tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) #undef RECURSE } +/* Implement c-family hook to add language-specific features + for __has_{feature,extension}. */ + +void +c_family_register_lang_features () +{ + objc_common_register_features (); + cp_register_features (); +} + static void objcxx_init_ts (void) { 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..1604d7790fc --- /dev/null +++ b/gcc/testsuite/c-c++-common/has-feature-common.c @@ -0,0 +1,73 @@ +/* { 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 !EXT (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 !EXT (__gnu_asm_goto_with_outputs_full__) +#error +#endif + +#if !FEAT (enumerator_attributes) +#error +#endif + +#if !FEAT (__enumerator_attributes__) +#error +#endif + +#if !FEAT (attribute_deprecated_with_message) +#error +#endif + +#if !FEAT (__attribute_deprecated_with_message__) +#error +#endif + +#if !FEAT (attribute_unavailable_with_message) +#error +#endif + +#if !FEAT (__attribute_unavailable_with_message__) +#error +#endif + +#if !FEAT (tls) +#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/c-c++-common/has-feature-pedantic.c b/gcc/testsuite/c-c++-common/has-feature-pedantic.c new file mode 100644 index 00000000000..4ac16a4ed8a --- /dev/null +++ b/gcc/testsuite/c-c++-common/has-feature-pedantic.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-pedantic-errors" } */ + +/* When -pedantic-errors is passed, __has_extension should behave like + __has_feature. */ + +#if __has_feature (gnu_asm_goto_with_outputs) +#error extension recognized as feature +#endif + +#if __has_extension (gnu_asm_goto_with_outputs) +#error pure extensions should not be recognized with -pedantic-errors +#endif + +#if !__has_feature (tls) || !__has_extension (tls) +#error features should still be recognized with -pedantic-errors +#endif + +/* Make this TU non-empty to appease -pedantic-errors. */ +int foo; 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 54814f335d7..0f4c71c48c6 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -762,6 +762,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); @@ -966,7 +969,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 250a02aa993..5dfce4bd7a0 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 a1a134ab3a0..7ba77fe88b3 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)