From patchwork Wed Nov 29 18:01:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 171537 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:a5a7:0:b0:403:3b70:6f57 with SMTP id d7csp536741vqn; Wed, 29 Nov 2023 10:32:34 -0800 (PST) X-Google-Smtp-Source: AGHT+IElmVjuZLbD4HO/ZvPbACZOJ5OIx6ilhOMkjCjwEfivcSx4pJSz2uAH+5qvRssagA8/MCUh X-Received: by 2002:ac8:5747:0:b0:413:3384:d43f with SMTP id 7-20020ac85747000000b004133384d43fmr27401638qtx.11.1701282754396; Wed, 29 Nov 2023 10:32:34 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1701282754; cv=pass; d=google.com; s=arc-20160816; b=C0SDJJz87edg8yxfYiPlAJMO3EE7NXuuud+c7yf4eAIBZ9aNTnm/UFcublRZCVaSV2 V+A+nnIU8BaY6cQCFEzlRk4mP1Nq8VSO18r9jHook0XcUUs4EUxMwohi+A9GHFLgT3PY udivyc0XZoZa7a+d36OUeCwYVkBArF2WNf8NwtO44krrmduQsugOic/Yie2GZuGFwrSw ViYceO+nT89aIZG+qgPcS/WoWlz1w4pBzAW2wcQ/+Hy6JSMMUdGKMneXqeFh2pWyaFAB ojVySDGONaMZSKpIpv/m8I4L1NE9oCfG8fx66ZaKnwmnYNuv89oMCd7NfTOhyjfXdsaw +vZw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:reply-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-disposition:in-reply-to :mime-version:references:message-id:subject:cc:to:from:date :resent-to:resent-message-id:resent-date:resent-from:dkim-signature :arc-filter:dmarc-filter:delivered-to; bh=PXzsyblxoMjF7bm4qFxUfDYX+KFcky5mgsj74p/4Ios=; fh=0xZT+NBKSeH8qOu04/61f1ZGePpF4jF/gxp331YE14k=; b=vL8eBx9HwNL33F9lSDMAGGhakSt/YhVsuHcLWy0wJmDsYkChdXPpmyRzsmyCdIKu/h QIyZX0wxOzCanoDwvu1VygUGTOdPp0tg00QnNvo51MTPcIKTenRa3d1/uIp4jTljyX3w 1mYkWtpYOGVk/Q0HmeiLZMP9q03dCKX4Mtu9g58MB52BFO/tv2b9JZuYKitsDqqtJZYA uuySUoVgfYhv6kwyS7l2QrLtfhFfokIcceAUADQrah8ptFwSfxTOzs387b1F/diRRSa9 kbb9thhsCUjnvGQpOUEsgoRlRYP9ytX3j/JhraEmkkdk+SJUZcrLPH3uvkqocXNlKg4z yVSw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Ej7tVc6m; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id j11-20020a05622a038b00b004236e9c2deasi13578809qtx.114.2023.11.29.10.32.34 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Nov 2023 10:32:34 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Ej7tVc6m; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0951B386CE56 for ; Wed, 29 Nov 2023 18:32:34 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 7AD363858C2F for ; Wed, 29 Nov 2023 18:31:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7AD363858C2F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7AD363858C2F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1701282720; cv=none; b=qwjIh6y34t/fXWqT97wVyDr2cy/FT5yjg04DGy/4VywdVZjTAkkOPoD44KaEPCOdIFFADWEmaQFJR2y42ctOX0Rp2dNObx2q16aOzgk0VSGZJMWfBQBlWEBS8o3NKqaSp3V/3DBvRZmokF2qhn1dbaOGen4AH9XnBY8/KGJVZ6A= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1701282720; c=relaxed/simple; bh=OyB0bNFPMWX6HCn+oiJyLAL+L4KF4RG6Z7Bfcm0JSVc=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=HTFyPDMItQtNw9k/PJ2LT4MD2wmABT+fYaNDFh7IYmfnKoSoehPvhdgVSymrx/2pf2DjlZqG1G2qpv8Iv4r9bpIhrixVRQKKMQoCXgsMgR48xNVh8yyp8cRWypNqSDZ66/hSeDEYnWSgJN09BGYrA74Vp828fcD+PdRBqYFcUc8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1701282714; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:resent-to:resent-from:resent-message-id: in-reply-to:in-reply-to:references:references; bh=PXzsyblxoMjF7bm4qFxUfDYX+KFcky5mgsj74p/4Ios=; b=Ej7tVc6mTm7cGp7hN0W8ERsz/zVFiCCU7ayPpefIn0ZQcBLpxSDEfEBHz8LCITa4OyGNmo Hmxk4uIUtKGLTy/xRgEqj59uWicmHZpkfGNl/io9gU2bkRm6gtUOoTHHgfZSu1IgPxA6s6 Er5XtqSspDqqMdEj7LzElv8qJ+RLYm8= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-544-F-FwehS7P6-uKBCFZasZuw-1; Wed, 29 Nov 2023 13:31:51 -0500 X-MC-Unique: F-FwehS7P6-uKBCFZasZuw-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 69D763C027B9 for ; Wed, 29 Nov 2023 18:31:51 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.195.157]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CE0A01C060AE for ; Wed, 29 Nov 2023 18:31:50 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 3ATIVmn93335099 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT) for ; Wed, 29 Nov 2023 19:31:48 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 3ATIVmmY3335098 for gcc-patches@gcc.gnu.org; Wed, 29 Nov 2023 19:31:48 +0100 Resent-From: Jakub Jelinek Resent-Date: Wed, 29 Nov 2023 19:31:48 +0100 Resent-Message-ID: Resent-To: gcc-patches@gcc.gnu.org Date: Wed, 29 Nov 2023 19:01:53 +0100 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++, v4: Implement C++26 P2169R4 - Placeholder variables with no name [PR110349] Message-ID: References: MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.7 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-3.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1783924265249869963 X-GMAIL-MSGID: 1783924265249869963 On Tue, Nov 28, 2023 at 11:27:55AM -0500, Jason Merrill wrote: > On 11/24/23 03:34, Jakub Jelinek wrote: > > On Mon, Sep 18, 2023 at 07:12:40PM +0200, Jakub Jelinek via Gcc-patches wrote: > > > On Tue, Aug 22, 2023 at 09:39:11AM +0200, Jakub Jelinek via Gcc-patches wrote: > > > > The following patch implements the C++26 P2169R4 paper. > > > > As written in the PR, the patch expects that: > > > > 1) https://eel.is/c++draft/expr.prim.lambda.capture#2 > > > > "Ignoring appearances in initializers of init-captures, an identifier > > > > or this shall not appear more than once in a lambda-capture." > > > > is adjusted such that name-independent lambda captures with initializers > > > > can violate this rule (but lambda captures which aren't name-independent > > > > can't appear after name-independent ones) > > > > 2) https://eel.is/c++draft/class.mem#general-5 > > > > "A member shall not be declared twice in the member-specification, > > > > except that" > > > > having an exception that name-independent non-static data member > > > > declarations can appear multiple times (but again, if there is > > > > a member which isn't name-independent, it can't appear after > > > > name-independent ones) > > > > 3) it assumes that any name-independent declarations which weren't > > > > previously valid result in the _ lookups being ambiguous, not just > > > > if there are 2 _ declarations in the same scope, in particular the > > > > https://eel.is/c++draft/basic.scope#block-2 mentioned cases > > > > 4) it assumes that _ in static function/block scope structured bindings > > > > is never name-independent like in namespace scope structured bindings; > > > > it matches clang behavior and is consistent with e.g. static type _; > > > > not being name-independent both at namespace scope and at function/block > > > > scope > > 2023-11-23 Jakub Jelinek > > > > PR c++/110349 > > gcc/c-family/ > > * c-cppbuiltin.cc (c_cpp_builtins): Predefine > > __cpp_placeholder_variables=202306L for C++26. > > gcc/cp/ > > * cp-tree.h: Implement C++26 P2169R4 - Placeholder variables with no > > name. > > (OVL_PLACEHOLDER_P): Define. > > Throughout this patch let's follow the standard and avoid using the word > "placeholder" to refer to name-independent decls. There are a lot of other > things that could be meant by "placeholder", notably "auto". Ok, changed it almost everywhere, except for the predefined macro which the C++ standard defines that way and the name of the P2169R4 paper in ChangeLog and test comments because the paper is called like that. > > + else > > + { > > Please add a comment here with your rationale from the commit message. Done. > > + binding->value = nreverse (binding->value); > > + /* Skip over TREE_LISTs added for check_local_shadow detected > > This should mention pushdecl, since that's where they're actually added > (which, as mentioned below, I don't understand why). Done. > > - check_local_shadow (decl); > > + tree local_shadow = check_local_shadow (decl); > > + if (placeholder_p && local_shadow) > > + { > > + if (cxx_dialect < cxx26 && !placeholder_diagnosed_p) > > + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wc__26_extensions, > > + "placeholder variables only available with " > > + "%<-std=c++2c%> or %<-std=gnu++2c%>"); > > + placeholder_diagnosed_p = true; > > + if (old == NULL_TREE) > > + { > > + old = build_tree_list (error_mark_node, local_shadow); > > + TREE_TYPE (old) = error_mark_node; > > + } > > + } > > This needs a rationale comment; I don't understand what the purpose is. Added it, but in short it is the assumption 3) above (which dunno if it is something that has been discussed or I should file as DR or you would), I believe it would be very weird if say for void foo (int x) { int x = 5; } the standard says it must be rejected even when the 2 x decls don't live in the same scope (ditto all other cases check_local_shadow deals with), while void foo (int _) { int _ = 5; return _; } would be accepted and void bar () { int _ = 4; int _ = 5; return _; } not. > > @@ -7579,7 +7800,30 @@ lookup_name (tree name, LOOK_where where > > && (bool (want & LOOK_want::HIDDEN_LAMBDA) > > || !is_lambda_ignored_entity (iter->value)) > > && qualify_lookup (iter->value, want)) > > - binding = iter->value; > > + { > > + binding = iter->value; > > + if (binding > > + && TREE_CODE (binding) == TREE_LIST > > + && name_independent_decl_p (TREE_VALUE (binding))) > > + { > > + for (tree b = binding; b; b = TREE_CHAIN (b)) > > + if (TREE_CHAIN (b) == NULL > > + && TREE_CODE (TREE_VALUE (b)) == OVERLOAD) > > + { > > + /* If the scope has an overload with _ function > > + declarations followed by at least one > > + name-independent declaration, we shouldn't return > > + iter->value but a new TREE_LIST containing the > > + name-independent declaration(s) and functions > > + from the OVERLOAD. */ > > Why is the iter->value TREE_LIST unsuitable? I thought the diagnostics routines to report the ambiguities would not be happy seeing OVERLOADs among the TREE_LIST, but seems removal of the above hunk just causes /usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:44:7: error: reference to '_' is ambiguous -/usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:42:18: note: candidates are: 'float _(float)' +/usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:43:9: note: candidates are: 'int _' +/usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:42:18: note: 'float _(float)' /usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:41:17: note: 'long int _(long int)' /usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:40:16: note: 'int _(int)' -/usr/src/gcc/gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C:43:9: note: 'int _' kind of changes (- with the hunk, + without), so I've dropped that hunk. > > + { > > + static int _ = 3; // { dg-warning "declaration of '_' shadows a previous local" } > > Perhaps we don't want to warn if either of the declarations is > name-independent? Done. > > + { > > + static auto [i, _, _] = a; // { dg-error "redeclaration of 'auto _'" } > > Would it be easy to add a note explaining that static static bindings aren't > name-independent? Tried to do that in duplicate_decls, but dunno if I've converted all the most important reasons why something might not be name-independent despite _ name (e.g. template parameter is not in the list) and whether the wording is right in all cases. And I've restricted the extra messages to C++26 and later, because in older standards the fact that we treat something as name-independent declaration is just an extension, pedantically nothing is name-independent. > > +namespace A > > +{ > > + int _ = 1; > > + int _ = 1; // { dg-error "redefinition of 'int A::_'" } > > Similarly, a note explaining that namespace-scope variables aren't > name-independent. And this. > > +void > > +corge (int _, int _) // { dg-error "redefinition of 'int _'" } > > ...likewise parameters. And this too... This patch passes GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ RUNTESTFLAGS="dg.exp=cpp26/*.C" but haven't tested it beyond that (of course will run it through full bootstrap/regtest). 2023-11-29 Jakub Jelinek PR c++/110349 gcc/c-family/ * c-cppbuiltin.cc (c_cpp_builtins): Predefine __cpp_placeholder_variables=202306L for C++26. gcc/cp/ * cp-tree.h: Implement C++26 P2169R4 - Placeholder variables with no name. (OVL_NAME_INDEPENDENT_DECL_P): Define. (add_capture): Add unsigned * argument. (name_independent_decl_p): New inline function. * name-lookup.cc (class name_lookup): Make ambiguous and add_value members public. (name_independent_linear_search): New function. (get_class_binding_direct): Handle member_vec_binary_search returning OVL_NAME_INDEPENDENT_DECL_P OVERLOAD. Use name_independent_linear_search rather than fields_linear_search for linear lookup of _ name if !want_type. (member_name_cmp): Sort name-independent declarations first. (member_vec_dedup): Handle name-independent declarations. (pop_local_binding): Handle binding->value being a TREE_LIST for ambiguous name-independent declarations. (supplement_binding): Handle name-independent declarations. (update_binding): Likewise. (check_local_shadow): Return tree rather than void, normally NULL_TREE but old for name-independent declarations which used to conflict with outer scope declaration. Don't emit -Wshadow* warnings for name-independent declarations. (pushdecl): Handle name-independent declarations. * search.cc (lookup_field_r): Handle nval being a TREE_LIST. * lambda.cc (build_capture_proxy): Adjust for ___. names of members. (add_capture): Add NAME_INDEPENDENT_CNT argument. Use ___. name rather than ___ for second and following capture with _ name. (add_default_capture): Adjust add_capture caller. * decl.cc (poplevel): Don't warn about name-independent declarations. (duplicate_decls): If in C++26 a _ named declaration conflicts with earlier declarations, emit explaining note why the new declaration is not name-independent. (reshape_init_class): If field is a TREE_LIST, emit an ambiguity error with list of candidates rather than error about non-existing non-static data member. * parser.cc (cp_parser_lambda_introducer): Adjust add_capture callers. Allow name-independent capture redeclarations. (cp_parser_decomposition_declaration): Set decl_specs.storage_class to sc_static for static structured bindings. * pt.cc (tsubst_lambda_expr): Adjust add_capture caller. gcc/testsuite/ * g++.dg/cpp26/name-independent-decl1.C: New test. * g++.dg/cpp26/name-independent-decl2.C: New test. * g++.dg/cpp26/name-independent-decl3.C: New test. * g++.dg/cpp26/name-independent-decl4.C: New test. * g++.dg/cpp26/name-independent-decl5.C: New test. * g++.dg/cpp26/name-independent-decl6.C: New test. * g++.dg/cpp26/feat-cxx26.C: Add __cpp_placeholder_variables test. Jakub --- gcc/c-family/c-cppbuiltin.cc.jj 2023-11-24 08:43:01.177963318 +0100 +++ gcc/c-family/c-cppbuiltin.cc 2023-11-29 15:51:49.331881183 +0100 @@ -1088,6 +1088,7 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++26. */ cpp_define (pfile, "__cpp_constexpr=202306L"); cpp_define (pfile, "__cpp_static_assert=202306L"); + cpp_define (pfile, "__cpp_placeholder_variables=202306L"); } if (flag_concepts) { --- gcc/cp/cp-tree.h.jj 2023-11-29 08:36:33.194393034 +0100 +++ gcc/cp/cp-tree.h 2023-11-29 15:51:49.332881169 +0100 @@ -523,6 +523,7 @@ extern GTY(()) tree cp_global_trees[CPTI RANGE_FOR_IVDEP (in RANGE_FOR_STMT) CALL_EXPR_OPERATOR_SYNTAX (in CALL_EXPR, AGGR_INIT_EXPR) CONSTRUCTOR_IS_DESIGNATED_INIT (in CONSTRUCTOR) + OVL_NAME_INDEPENDENT_DECL_P (in OVERLOAD) Usage of TYPE_LANG_FLAG_?: 0: TYPE_DEPENDENT_P @@ -815,6 +816,9 @@ typedef struct ptrmem_cst * ptrmem_cst_t #define OVL_LOOKUP_P(NODE) TREE_LANG_FLAG_4 (OVERLOAD_CHECK (NODE)) /* If set, this OVL_USING_P overload is exported. */ #define OVL_EXPORT_P(NODE) TREE_LANG_FLAG_5 (OVERLOAD_CHECK (NODE)) +/* If set, this overload includes name-independent declarations. */ +#define OVL_NAME_INDEPENDENT_DECL_P(NODE) \ + TREE_LANG_FLAG_6 (OVERLOAD_CHECK (NODE)) /* The first decl of an overload. */ #define OVL_FIRST(NODE) ovl_first (NODE) @@ -7864,7 +7868,7 @@ extern tree lambda_capture_field_type ( extern tree lambda_proxy_type (tree); extern tree lambda_function (tree); extern void apply_deduced_return_type (tree, tree); -extern tree add_capture (tree, tree, tree, bool, bool); +extern tree add_capture (tree, tree, tree, bool, bool, unsigned *); extern tree add_default_capture (tree, tree, tree); extern void insert_capture_proxy (tree); extern void insert_pending_capture_proxies (void); @@ -8930,6 +8934,18 @@ extended_float_type_p (tree type) return false; } +/* True if DECL is name-independent declaration. */ + +inline bool +name_independent_decl_p (tree decl) +{ + return ((VAR_P (decl) || TREE_CODE (decl) == FIELD_DECL) + && DECL_NAME (decl) + && id_equal (DECL_NAME (decl), "_") + && !TREE_STATIC (decl) + && !DECL_EXTERNAL (decl)); +} + #if CHECKING_P namespace selftest { extern void run_cp_tests (void); --- gcc/cp/name-lookup.cc.jj 2023-11-25 10:28:27.747191994 +0100 +++ gcc/cp/name-lookup.cc 2023-11-29 17:26:12.143505568 +0100 @@ -511,10 +511,11 @@ private: void preserve_state (); void restore_state (); -private: +public: static tree ambiguous (tree thing, tree current); - void add_overload (tree fns); void add_value (tree new_val); +private: + void add_overload (tree fns); void add_type (tree new_type); bool process_binding (tree val_bind, tree type_bind); unsigned process_module_binding (tree val_bind, tree type_bind, unsigned); @@ -1806,6 +1807,71 @@ fields_linear_search (tree klass, tree n return NULL_TREE; } +/* Like fields_linear_search, but specific for "_" name. There can be multiple + name-independent non-static data members and in that case a TREE_LIST with the + ambiguous decls should be returned. */ + +static tree +name_independent_linear_search (tree val, tree klass, tree name) +{ + for (tree fields = TYPE_FIELDS (klass); fields; fields = DECL_CHAIN (fields)) + { + tree decl = fields; + + if (TREE_CODE (decl) == FIELD_DECL + && ANON_AGGR_TYPE_P (TREE_TYPE (decl))) + { + if (tree temp = search_anon_aggr (TREE_TYPE (decl), name, false)) + { + decl = temp; + goto add; + } + } + + if (DECL_NAME (decl) != name) + continue; + + if (TREE_CODE (decl) == USING_DECL) + { + decl = strip_using_decl (decl); + if (is_overloaded_fn (decl)) + continue; + } + + if (DECL_DECLARES_FUNCTION_P (decl)) + /* Functions are found separately. */ + continue; + + add: + if (val == NULL_TREE) + val = decl; + else + { + if (TREE_CODE (val) != TREE_LIST) + { + if (TREE_CODE (val) == OVERLOAD + && OVL_DEDUP_P (val) + && TREE_CODE (decl) == USING_DECL) + { + val = ovl_make (decl, val); + continue; + } + val = tree_cons (NULL_TREE, val, NULL_TREE); + TREE_TYPE (val) = error_mark_node; + } + if (TREE_CODE (decl) == TREE_LIST) + val = chainon (decl, val); + else + { + val = tree_cons (NULL_TREE, decl, val); + TREE_TYPE (val) = error_mark_node; + } + } + } + + return val; +} + /* Look for NAME member inside of anonymous aggregate ANON. Although such things should only contain FIELD_DECLs, we check that too late, and would give very confusing errors if we weren't @@ -1843,6 +1909,50 @@ get_class_binding_direct (tree klass, tr val = member_vec_binary_search (member_vec, lookup); if (!val) ; + else if (TREE_CODE (val) == OVERLOAD + && OVL_NAME_INDEPENDENT_DECL_P (val)) + { + if (want_type) + { + while (TREE_CODE (val) == OVERLOAD + && OVL_NAME_INDEPENDENT_DECL_P (val)) + val = OVL_CHAIN (val); + if (STAT_HACK_P (val)) + val = STAT_TYPE (val); + else if (!DECL_DECLARES_TYPE_P (val)) + val = NULL_TREE; + } + else + { + /* OVERLOAD with a special OVL_NAME_INDEPENDENT_DECL_P + flag is used under the hood to represent lookup + results which include name-independent declarations, + and get_class_binding_direct is turning that into + TREE_LIST representation (which the callers expect for + ambiguous lookups) instead. + There are 2 reasons for that: + 1) in order to keep the member_vec binary search fast, I + think it is better to keep OVL_NAME usable on all elements + because having to special case TREE_LIST would slow + everything down; + 2) the callers need to be able to chain the results anyway + and so need an unshared TREE_LIST they can tweak/destroy. */ + tree ovl = val; + val = NULL_TREE; + while (TREE_CODE (ovl) == OVERLOAD + && OVL_NAME_INDEPENDENT_DECL_P (ovl)) + { + val = tree_cons (NULL_TREE, OVL_FUNCTION (ovl), val); + TREE_TYPE (val) = error_mark_node; + ovl = OVL_CHAIN (ovl); + } + if (STAT_HACK_P (ovl)) + val = tree_cons (NULL_TREE, STAT_DECL (ovl), val); + else + val = tree_cons (NULL_TREE, ovl, val); + TREE_TYPE (val) = error_mark_node; + } + } else if (STAT_HACK_P (val)) val = want_type ? STAT_TYPE (val) : STAT_DECL (val); else if (want_type && !DECL_DECLARES_TYPE_P (val)) @@ -1853,7 +1963,9 @@ get_class_binding_direct (tree klass, tr if (member_vec && !want_type) val = member_vec_linear_search (member_vec, lookup); - if (!val || (TREE_CODE (val) == OVERLOAD && OVL_DEDUP_P (val))) + if (id_equal (lookup, "_") && !want_type) + val = name_independent_linear_search (val, klass, lookup); + else if (!val || (TREE_CODE (val) == OVERLOAD && OVL_DEDUP_P (val))) /* Dependent using declarations are a 'field', make sure we return that even if we saw an overload already. */ if (tree field_val = fields_linear_search (klass, lookup, want_type)) @@ -2049,6 +2161,25 @@ member_name_cmp (const void *a_p, const if (TREE_CODE (b) == OVERLOAD) b = OVL_FUNCTION (b); + if (id_equal (name_a, "_")) + { + /* Sort name-independent members first. */ + if (name_independent_decl_p (a)) + { + if (name_independent_decl_p (b)) + { + if (DECL_UID (a) != DECL_UID (b)) + return DECL_UID (a) < DECL_UID (b) ? -1 : +1; + gcc_assert (a == b); + return 0; + } + else + return -1; + } + else if (name_independent_decl_p (b)) + return +1; + } + /* We're in STAT_HACK or USING_DECL territory (or possibly error-land). */ if (TREE_CODE (a) != TREE_CODE (b)) { @@ -2183,14 +2314,15 @@ member_vec_append_enum_values (vec */ + */ static void member_vec_dedup (vec *member_vec) @@ -2208,6 +2340,7 @@ member_vec_dedup (vec *memb tree to_type = NULL_TREE; tree to_using = NULL_TREE; tree marker = NULL_TREE; + unsigned name_independent = ix; for (jx = ix; jx < len; jx++) { @@ -2251,7 +2384,9 @@ member_vec_dedup (vec *memb continue; } - if (!current) + if (name_independent_decl_p (next)) + name_independent = jx + 1; + else if (!current) current = next; } @@ -2271,6 +2406,17 @@ member_vec_dedup (vec *memb current = stat_hack (current, to_type); } + for (unsigned kx = name_independent; kx > ix; --kx) + if (!current) + current = (*member_vec)[kx - 1]; + else if (current == to_type) + current = stat_hack ((*member_vec)[kx - 1], to_type); + else + { + current = ovl_make ((*member_vec)[kx - 1], current); + OVL_NAME_INDEPENDENT_DECL_P (current) = 1; + } + if (current) { if (marker) @@ -2479,10 +2625,27 @@ pop_local_binding (tree id, tree decl) away. */ if (binding->value == decl) binding->value = NULL_TREE; + else if (binding->type == decl) + binding->type = NULL_TREE; else { - gcc_checking_assert (binding->type == decl); - binding->type = NULL_TREE; + /* Name-independent variable was found after at least one declaration + with the same name. */ + gcc_assert (TREE_CODE (binding->value) == TREE_LIST); + if (TREE_VALUE (binding->value) != decl) + { + binding->value = nreverse (binding->value); + /* Skip over TREE_LISTs added in pushdecl for check_local_shadow + detected declarations, formerly at the tail, now at the start + of the list. */ + while (TREE_PURPOSE (binding->value) == error_mark_node) + binding->value = TREE_CHAIN (binding->value); + } + gcc_assert (TREE_VALUE (binding->value) == decl); + binding->value = TREE_CHAIN (binding->value); + while (binding->value + && TREE_PURPOSE (binding->value) == error_mark_node) + binding->value = TREE_CHAIN (binding->value); } if (!binding->value && !binding->type) @@ -2579,6 +2742,10 @@ supplement_binding (cxx_binding *binding tree bval = binding->value; bool ok = true; + if (bval + && TREE_CODE (bval) == TREE_LIST + && name_independent_decl_p (TREE_VALUE (bval))) + bval = TREE_VALUE (bval); tree target_bval = strip_using_decl (bval); tree target_decl = strip_using_decl (decl); @@ -2682,6 +2849,14 @@ supplement_binding (cxx_binding *binding && CONST_DECL_USING_P (decl)) /* Let the clone hide the using-decl that introduced it. */ binding->value = decl; + else if (name_independent_decl_p (decl)) + { + if (cxx_dialect < cxx26) + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wc__26_extensions, + "name-independent declarations only available with " + "%<-std=c++2c%> or %<-std=gnu++2c%>"); + binding->value = name_lookup::ambiguous (decl, binding->value); + } else { if (!error_operand_p (bval)) @@ -2786,6 +2961,7 @@ update_binding (cp_binding_level *level, tree old_type = NULL_TREE; bool hide_type = false; bool hide_value = false; + bool name_independent_p = false; if (!slot) { @@ -2793,6 +2969,7 @@ update_binding (cp_binding_level *level, hide_type = HIDDEN_TYPE_BINDING_P (binding); if (!old_type) hide_value = hide_type, hide_type = false; + name_independent_p = name_independent_decl_p (decl); } else if (STAT_HACK_P (*slot)) { @@ -2888,7 +3065,9 @@ update_binding (cp_binding_level *level, } else if (old) { - if (TREE_CODE (old) != TREE_CODE (decl)) + if (name_independent_p) + to_val = name_lookup::ambiguous (decl, old); + else if (TREE_CODE (old) != TREE_CODE (decl)) /* Different kinds of decls conflict. */ goto conflict; else if (TREE_CODE (old) == TYPE_DECL) @@ -3088,13 +3267,13 @@ inform_shadowed (tree shadowed) /* DECL is being declared at a local scope. Emit suitable shadow warnings. */ -static void +static tree check_local_shadow (tree decl) { /* Don't complain about the parms we push and then pop while tentatively parsing a function declarator. */ if (TREE_CODE (decl) == PARM_DECL && !DECL_CONTEXT (decl)) - return; + return NULL_TREE; tree old = NULL_TREE; cp_binding_level *old_scope = NULL; @@ -3129,7 +3308,7 @@ check_local_shadow (tree decl) error_at (DECL_SOURCE_LOCATION (old), "lambda parameter %qD " "previously declared as a capture", old); - return; + return NULL_TREE; } /* Don't complain if it's from an enclosing function. */ else if (DECL_CONTEXT (old) == current_function_decl @@ -3153,6 +3332,9 @@ check_local_shadow (tree decl) in the outermost block of the function definition. */ if (b->kind == sk_function_parms) { + if (name_independent_decl_p (decl)) + return old; + auto_diagnostic_group d; bool emit = true; if (DECL_EXTERNAL (decl)) @@ -3165,7 +3347,7 @@ check_local_shadow (tree decl) if (emit) inform (DECL_SOURCE_LOCATION (old), "%q#D previously declared here", old); - return; + return NULL_TREE; } } @@ -3177,7 +3359,7 @@ check_local_shadow (tree decl) scope != old_scope; scope = scope->level_chain) if (scope->kind == sk_class && !LAMBDA_TYPE_P (scope->this_entity)) - return; + return NULL_TREE; } /* Error if redeclaring a local declared in a init-statement or in the condition of an if or @@ -3189,6 +3371,9 @@ check_local_shadow (tree decl) && old_scope == current_binding_level->level_chain && (old_scope->kind == sk_cond || old_scope->kind == sk_for)) { + if (name_independent_decl_p (decl)) + return old; + auto_diagnostic_group d; bool emit = true; if (DECL_EXTERNAL (decl)) @@ -3200,7 +3385,7 @@ check_local_shadow (tree decl) if (emit) inform (DECL_SOURCE_LOCATION (old), "%q#D previously declared here", old); - return; + return NULL_TREE; } /* C++11: 3.3.3/3: The name declared in an exception-declaration (...) @@ -3212,6 +3397,9 @@ check_local_shadow (tree decl) && old_scope == current_binding_level->level_chain && old_scope->kind == sk_catch) { + if (name_independent_decl_p (decl)) + return old; + auto_diagnostic_group d; bool emit; if (DECL_EXTERNAL (decl)) @@ -3223,9 +3411,13 @@ check_local_shadow (tree decl) if (emit) inform (DECL_SOURCE_LOCATION (old), "%q#D previously declared here", old); - return; + return NULL_TREE; } + /* Don't emit -Wshadow* warnings for name-independent decls. */ + if (name_independent_decl_p (decl) || name_independent_decl_p (old)) + return NULL_TREE; + /* If '-Wshadow=compatible-local' is specified without other -Wshadow= flags, we will warn only when the type of the shadowing variable (DECL) can be converted to that of the @@ -3278,15 +3470,19 @@ check_local_shadow (tree decl) auto_diagnostic_group d; if (warning_at (DECL_SOURCE_LOCATION (decl), warning_code, msg, decl)) inform_shadowed (old); - return; + return NULL_TREE; } if (!warn_shadow) - return; + return NULL_TREE; + + /* Don't emit -Wshadow for name-independent decls. */ + if (name_independent_decl_p (decl)) + return NULL_TREE; /* Don't warn for artificial things that are not implicit typedefs. */ if (DECL_ARTIFICIAL (decl) && !DECL_IMPLICIT_TYPEDEF_P (decl)) - return; + return NULL_TREE; if (nonlambda_method_basetype ()) if (tree member = lookup_member (current_nonlambda_class_type (), @@ -3314,7 +3510,7 @@ check_local_shadow (tree decl) suppress_warning (decl, OPT_Wshadow); } } - return; + return NULL_TREE; } /* Now look for a namespace shadow. */ @@ -3337,10 +3533,10 @@ check_local_shadow (tree decl) inform_shadowed (old); suppress_warning (decl, OPT_Wshadow); } - return; + return NULL_TREE; } - return; + return NULL_TREE; } /* DECL is being pushed inside function CTX. Set its context, if @@ -3659,6 +3855,8 @@ pushdecl (tree decl, bool hiding) tree *slot = NULL; /* Binding slot in namespace. */ tree *mslot = NULL; /* Current module slot in namespace. */ tree old = NULL_TREE; + bool name_independent_p = false; + bool name_independent_diagnosed_p = false; if (level->kind == sk_namespace) { @@ -3682,56 +3880,82 @@ pushdecl (tree decl, bool hiding) binding = find_local_binding (level, name); if (binding) old = binding->value; + name_independent_p = name_independent_decl_p (decl); } if (old == error_mark_node) old = NULL_TREE; - for (ovl_iterator iter (old); iter; ++iter) - if (iter.using_p ()) - ; /* Ignore using decls here. */ - else if (iter.hidden_p () - && TREE_CODE (*iter) == FUNCTION_DECL - && DECL_LANG_SPECIFIC (*iter) - && DECL_MODULE_IMPORT_P (*iter)) - ; /* An undeclared builtin imported from elsewhere. */ - else if (tree match - = duplicate_decls (decl, *iter, hiding, iter.hidden_p ())) - { - if (match == error_mark_node) - ; - else if (TREE_CODE (match) == TYPE_DECL) - gcc_checking_assert (REAL_IDENTIFIER_TYPE_VALUE (name) - == (level->kind == sk_namespace - ? NULL_TREE : TREE_TYPE (match))); - else if (iter.hidden_p () && !hiding) + tree oldi, oldn; + for (oldi = old; oldi; oldi = oldn) + { + if (TREE_CODE (oldi) == TREE_LIST) + { + gcc_checking_assert (level->kind != sk_namespace + && name_independent_decl_p + (TREE_VALUE (old))); + oldn = TREE_CHAIN (oldi); + oldi = TREE_VALUE (oldi); + } + else + oldn = NULL_TREE; + for (ovl_iterator iter (oldi); iter; ++iter) + if (iter.using_p ()) + ; /* Ignore using decls here. */ + else if (iter.hidden_p () + && TREE_CODE (*iter) == FUNCTION_DECL + && DECL_LANG_SPECIFIC (*iter) + && DECL_MODULE_IMPORT_P (*iter)) + ; /* An undeclared builtin imported from elsewhere. */ + else if (name_independent_p) { - /* Unhiding a previously hidden decl. */ - tree head = iter.reveal_node (old); - if (head != old) + /* Ignore name-independent declarations. */ + if (cxx_dialect < cxx26 && !name_independent_diagnosed_p) + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wc__26_extensions, + "name-independent declarations only available with " + "%<-std=c++2c%> or %<-std=gnu++2c%>"); + name_independent_diagnosed_p = true; + } + else if (tree match + = duplicate_decls (decl, *iter, hiding, iter.hidden_p ())) + { + if (match == error_mark_node) + ; + else if (TREE_CODE (match) == TYPE_DECL) + gcc_checking_assert (REAL_IDENTIFIER_TYPE_VALUE (name) + == (level->kind == sk_namespace + ? NULL_TREE : TREE_TYPE (match))); + else if (iter.hidden_p () && !hiding) { - gcc_checking_assert (ns); - if (STAT_HACK_P (*slot)) - STAT_DECL (*slot) = head; + /* Unhiding a previously hidden decl. */ + tree head = iter.reveal_node (oldi); + if (head != oldi) + { + gcc_checking_assert (ns); + if (STAT_HACK_P (*slot)) + STAT_DECL (*slot) = head; + else + *slot = head; + } + if (DECL_EXTERN_C_P (match)) + /* We need to check and register the decl now. */ + check_extern_c_conflict (match); + } + else if (slot + && !hiding + && STAT_HACK_P (*slot) + && STAT_DECL_HIDDEN_P (*slot)) + { + /* Unhide the non-function. */ + gcc_checking_assert (oldi == match); + if (!STAT_TYPE (*slot)) + *slot = match; else - *slot = head; + STAT_DECL (*slot) = match; } - if (DECL_EXTERN_C_P (match)) - /* We need to check and register the decl now. */ - check_extern_c_conflict (match); - } - else if (slot && !hiding - && STAT_HACK_P (*slot) && STAT_DECL_HIDDEN_P (*slot)) - { - /* Unhide the non-function. */ - gcc_checking_assert (old == match); - if (!STAT_TYPE (*slot)) - *slot = match; - else - STAT_DECL (*slot) = match; + return match; } - return match; - } + } /* Check for redeclaring an import. */ if (slot && *slot && TREE_CODE (*slot) == BINDING_VECTOR) @@ -3780,7 +4004,28 @@ pushdecl (tree decl, bool hiding) if (level->kind != sk_namespace) { - check_local_shadow (decl); + tree local_shadow = check_local_shadow (decl); + if (name_independent_p && local_shadow) + { + if (cxx_dialect < cxx26 && !name_independent_diagnosed_p) + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wc__26_extensions, + "name-independent declarations only available with " + "%<-std=c++2c%> or %<-std=gnu++2c%>"); + name_independent_diagnosed_p = true; + /* When a name-independent declaration is pushed into a scope + which itself does not contain a _ named declaration yet (so + _ name lookups wouldn't be normally ambiguous), but it + shadows a _ declaration in some outer scope in cases + described in [basic.scope.block]/2 where if the names of + the shadowed and shadowing declarations were different it + would be ill-formed program, arrange for _ name lookups + in this scope to be ambiguous. */ + if (old == NULL_TREE) + { + old = build_tree_list (error_mark_node, local_shadow); + TREE_TYPE (old) = error_mark_node; + } + } if (TREE_CODE (decl) == NAMESPACE_DECL) /* A local namespace alias. */ --- gcc/cp/search.cc.jj 2023-11-24 08:43:01.665956431 +0100 +++ gcc/cp/search.cc 2023-11-29 15:51:49.363880733 +0100 @@ -1091,13 +1091,24 @@ lookup_field_r (tree binfo, void *data) } /* Add the new value. */ - lfi->ambiguous = tree_cons (NULL_TREE, nval, lfi->ambiguous); - TREE_TYPE (lfi->ambiguous) = error_mark_node; + if (TREE_CODE (nval) == TREE_LIST) + lfi->ambiguous = chainon (nval, lfi->ambiguous); + else + { + lfi->ambiguous = tree_cons (NULL_TREE, nval, lfi->ambiguous); + TREE_TYPE (lfi->ambiguous) = error_mark_node; + } } } else { - lfi->rval = nval; + if (TREE_CODE (nval) == TREE_LIST) + { + lfi->ambiguous = chainon (nval, lfi->ambiguous); + lfi->rval = TREE_VALUE (nval); + } + else + lfi->rval = nval; lfi->rval_binfo = binfo; } --- gcc/cp/lambda.cc.jj 2023-11-24 08:43:01.369960609 +0100 +++ gcc/cp/lambda.cc 2023-11-29 15:51:49.363880733 +0100 @@ -412,7 +412,11 @@ build_capture_proxy (tree member, tree i object = TREE_OPERAND (object, 0); /* Remove the __ inserted by add_capture. */ - name = get_identifier (IDENTIFIER_POINTER (DECL_NAME (member)) + 2); + if (IDENTIFIER_POINTER (DECL_NAME (member))[2] == '_' + && IDENTIFIER_POINTER (DECL_NAME (member))[3] == '.') + name = get_identifier ("_"); + else + name = get_identifier (IDENTIFIER_POINTER (DECL_NAME (member)) + 2); type = lambda_proxy_type (object); @@ -516,7 +520,7 @@ vla_capture_type (tree array_type) tree add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p, - bool explicit_init_p) + bool explicit_init_p, unsigned *name_independent_cnt) { char *buf; tree type, member, name; @@ -610,11 +614,28 @@ add_capture (tree lambda, tree id, tree won't find the field with name lookup. We can't just leave the name unset because template instantiation uses the name to find instantiated fields. */ - buf = (char *) alloca (IDENTIFIER_LENGTH (id) + 3); - buf[1] = buf[0] = '_'; - memcpy (buf + 2, IDENTIFIER_POINTER (id), - IDENTIFIER_LENGTH (id) + 1); - name = get_identifier (buf); + if (id_equal (id, "_") && name_independent_cnt) + { + if (*name_independent_cnt == 0) + name = get_identifier ("___"); + else + { + /* For 2nd and later name-independent capture use + unique names. */ + char buf2[5 + (HOST_BITS_PER_INT + 2) / 3]; + sprintf (buf2, "___.%u", *name_independent_cnt); + name = get_identifier (buf2); + } + name_independent_cnt[0]++; + } + else + { + buf = XALLOCAVEC (char, IDENTIFIER_LENGTH (id) + 3); + buf[1] = buf[0] = '_'; + memcpy (buf + 2, IDENTIFIER_POINTER (id), + IDENTIFIER_LENGTH (id) + 1); + name = get_identifier (buf); + } if (variadic) { @@ -718,7 +739,7 @@ add_default_capture (tree lambda_stack, (this_capture_p || (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) == CPLD_REFERENCE)), - /*explicit_init_p=*/false); + /*explicit_init_p=*/false, NULL); initializer = convert_from_reference (var); /* Warn about deprecated implicit capture of this via [=]. */ --- gcc/cp/decl.cc.jj 2023-11-29 08:36:33.204392893 +0100 +++ gcc/cp/decl.cc 2023-11-29 17:56:31.299848688 +0100 @@ -680,6 +680,8 @@ poplevel (int keep, int reverse, int fun subobjects. */ && (DECL_DECOMPOSITION_P (decl) ? !DECL_DECOMP_BASE (decl) : (DECL_NAME (decl) && !DECL_ARTIFICIAL (decl))) + /* Don't warn about name-independent declarations. */ + && !name_independent_decl_p (decl) && type != error_mark_node && (!CLASS_TYPE_P (type) || !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) @@ -2063,6 +2065,44 @@ duplicate_decls (tree newdecl, tree oldd (DECL_INITIAL (olddecl) && namespace_bindings_p ()) ? G_("%q#D previously defined here") : G_("%q#D previously declared here"), olddecl); + if (cxx_dialect >= cxx26 + && DECL_NAME (newdecl) + && id_equal (DECL_NAME (newdecl), "_") + && !name_independent_decl_p (newdecl)) + { + if (TREE_CODE (newdecl) == PARM_DECL) + inform (newdecl_loc, + "parameter declaration is not name-independent"); + else if (DECL_DECOMPOSITION_P (newdecl)) + { + if (at_namespace_scope_p ()) + inform (newdecl_loc, + "structured binding at namespace scope is not " + "name-independent"); + else if (TREE_STATIC (newdecl)) + inform (newdecl_loc, + "static structured binding is not " + "name-independent"); + else if (DECL_EXTERNAL (newdecl)) + inform (newdecl_loc, + "extern structured binding is not " + "name-independent"); + } + else if (at_class_scope_p () + && VAR_P (newdecl) + && TREE_STATIC (newdecl)) + inform (newdecl_loc, + "static data member is not name-independent"); + else if (VAR_P (newdecl) && at_namespace_scope_p ()) + inform (newdecl_loc, + "variable at namespace scope is not name-independent"); + else if (VAR_P (newdecl) && TREE_STATIC (newdecl)) + inform (newdecl_loc, + "static variable is not name-independent"); + else if (VAR_P (newdecl) && DECL_EXTERNAL (newdecl)) + inform (newdecl_loc, + "extern variable is not name-independent"); + } return error_mark_node; } else if (TREE_CODE (olddecl) == FUNCTION_DECL @@ -6869,8 +6909,17 @@ reshape_init_class (tree type, reshape_i if (!field || TREE_CODE (field) != FIELD_DECL) { if (complain & tf_error) - error ("%qT has no non-static data member named %qD", type, - d->cur->index); + { + if (field && TREE_CODE (field) == TREE_LIST) + { + error ("request for member %qD is ambiguous", + d->cur->index); + print_candidates (field); + } + else + error ("%qT has no non-static data member named %qD", type, + d->cur->index); + } return error_mark_node; } --- gcc/cp/parser.cc.jj 2023-11-25 10:28:27.761191798 +0100 +++ gcc/cp/parser.cc 2023-11-29 15:51:49.382880464 +0100 @@ -11381,6 +11381,7 @@ cp_parser_lambda_introducer (cp_parser* hash_set ids; tree first_capture_id = NULL_TREE; + unsigned name_independent_cnt = 0; while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE)) { cp_token* capture_token; @@ -11425,7 +11426,7 @@ cp_parser_lambda_introducer (cp_parser* else add_capture (lambda_expr, /*id=*/this_identifier, /*initializer=*/finish_this_expr (), - /*by_reference_p=*/true, explicit_init_p); + /*by_reference_p=*/true, explicit_init_p, NULL); continue; } @@ -11447,7 +11448,7 @@ cp_parser_lambda_introducer (cp_parser* else add_capture (lambda_expr, /*id=*/this_identifier, /*initializer=*/finish_this_expr (), - /*by_reference_p=*/false, explicit_init_p); + /*by_reference_p=*/false, explicit_init_p, NULL); continue; } @@ -11634,13 +11635,15 @@ cp_parser_lambda_introducer (cp_parser* ids.add (first_capture_id); ids.add (capture_id); } + if (found && explicit_init_p && id_equal (capture_id, "_")) + found = false; if (found) pedwarn (input_location, 0, "already captured %qD in lambda expression", capture_id); else add_capture (lambda_expr, capture_id, capture_init_expr, /*by_reference_p=*/capture_kind == BY_REFERENCE, - explicit_init_p); + explicit_init_p, &name_independent_cnt); /* If there is any qualification still in effect, clear it now; we will be starting fresh with the next capture. */ @@ -15874,6 +15877,8 @@ cp_parser_decomposition_declaration (cp_ cp_decl_specifier_seq decl_specs; clear_decl_specs (&decl_specs); decl_specs.type = make_auto (); + if (decl_specifiers->storage_class == sc_static) + decl_specs.storage_class = sc_static; tree prev = decl; FOR_EACH_VEC_ELT (v, i, e) { --- gcc/cp/pt.cc.jj 2023-11-24 08:43:01.611957193 +0100 +++ gcc/cp/pt.cc 2023-11-29 15:51:49.400880210 +0100 @@ -19354,7 +19354,7 @@ tsubst_lambda_expr (tree t, tree args, t && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); vec* field_packs = NULL; - + unsigned name_independent_cnt = 0; for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t); cap; cap = TREE_CHAIN (cap)) { @@ -19384,7 +19384,8 @@ tsubst_lambda_expr (tree t, tree args, t bool by_ref = (TYPE_REF_P (ftype) || (TREE_CODE (ftype) == DECLTYPE_TYPE && DECLTYPE_FOR_REF_CAPTURE (ftype))); - add_capture (r, name, init, by_ref, !DECL_NORMAL_CAPTURE_P (ofield)); + add_capture (r, name, init, by_ref, !DECL_NORMAL_CAPTURE_P (ofield), + &name_independent_cnt); continue; } --- gcc/testsuite/g++.dg/cpp26/name-independent-decl1.C.jj 2023-11-29 15:51:49.401880196 +0100 +++ gcc/testsuite/g++.dg/cpp26/name-independent-decl1.C 2023-11-29 17:27:00.894799971 +0100 @@ -0,0 +1,194 @@ +// P2169R4 - A nice placeholder with no name +// { dg-do compile { target c++11 } } +// { dg-options "-Wunused-variable -Wunused-but-set-variable -Wunused-parameter -Wshadow" } + +int a[3]; + +void +foo () +{ + { + int _ = 1; + ++_; + } + { + int _ = 3; + ++_; + int _ = 4; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + { + int _ = 5; + --_; + int _ = 6; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + int _ = 7; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + { + auto [i, j, _] = a; // { dg-warning "structured bindings only available with" "" { target c++14_down } } + ++i; + ++_; + } + { + auto [_, _, k] = a; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++k; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } + } + { + auto [i, j, _] = a; // { dg-warning "structured bindings only available with" "" { target c++14_down } } + auto [_, k, l] = a; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++i; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } + ++l; + } + { + int _; + _ = 1; + } + { + int _ = 1; + } + { + int _; + } + { + static int _; // { dg-warning "unused variable" } + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + { + extern int _ (int); + extern long _ (long); + extern float _ (float); + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + { + extern double _ (double); + extern short _ (short); + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + int _ = 2; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + { + int _ = 1; + { + int _ = 2; + ++_; + } + { + static int _ = 3; + ++_; + } + { + auto [i, j, _] = a; // { dg-warning "structured bindings only available with" "" { target c++14_down } } + ++_; + } + } +} + +int +bar (int _ = 0) // { dg-warning "unused parameter '_'" } +{ + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + return 0; +} + +void +baz () +{ + if (int _ = bar ()) + int _ = 2; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + else + int _ = 3; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + while (int _ = bar ()) + int _ = 4; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + for (int _ = bar (); _; ++_) + int _ = 5; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + if (int _ = bar ()) + { + int _ = 6; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + else + { + int _ = 7; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + while (int _ = bar ()) + { + int _ = 8; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } + for (int _ = bar (); _; ++_) + { + int _ = 9; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } +} + +void +qux (short _ = 0) // { dg-warning "unused parameter '_'" } +{ + { + long _ = 1; + } +} + +void +corge () +{ + auto b = [_ = 1] () { (void) _; }; // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } } + // { dg-warning "variable 'b' set but not used" "" { target *-*-* } .-1 } + auto c = [_ = 2, _ = 3] () {};// { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } .-1 } + // { dg-warning "variable 'c' set but not used" "" { target *-*-* } .-2 } + { + int _ = 4; + auto d = [_, _ = 5] () {}; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } .-1 } + // { dg-warning "variable 'd' set but not used" "" { target *-*-* } .-2 } + { + int _ = 5; + auto e = [_ = 6] () {}; // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } } + } // { dg-warning "variable 'e' set but not used" "" { target *-*-* } .-1 } +} + +namespace A { + int _ = 11; +} + +void +garply (int x, // { dg-warning "unused parameter 'x'" } + int _, // { dg-warning "unused parameter '_'" } + int) +{ +} + +void +fred () +{ + try { + } catch (int _) { + int _ = 5; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } +} + +void +waldo (int _) // { dg-warning "unused parameter '_'" } +try +{ +} +catch (int _) // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +{ + int _ = 7; +} + +void +grault (int _) // { dg-warning "unused parameter '_'" } +try +{ +} +catch (int) +{ + int _ = 8; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +} + +void +plugh (int _) // { dg-warning "unused parameter '_'" } +try +{ + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +} +catch (int) +{ +} --- gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C.jj 2023-11-29 15:51:49.401880196 +0100 +++ gcc/testsuite/g++.dg/cpp26/name-independent-decl2.C 2023-11-29 18:03:02.352295830 +0100 @@ -0,0 +1,171 @@ +// P2169R4 - A nice placeholder with no name +// { dg-do compile { target c++11 } } +// { dg-options "" } + +int a[3]; + +void +foo () +{ + { + extern int _ (int); + int _ = 2; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + extern long _ (long); // { dg-error "redeclared as different kind of entity" } + } + { + int _ = 3; + extern int _ (int); // { dg-error "redeclared as different kind of entity" } + } + { + int _ = 4; + static int _ = 5; // { dg-error "redeclaration of 'int _'" } + } // { dg-message "static variable is not name-independent" "" { target c++26 } .-1 } + { + int _ = 6; + int _ = 7; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + int _ = 8; + int _ = 9; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + int _ = 10; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + static int _ = 11; + static int _ = 12; // { dg-error "redeclaration of 'int _'" } + int _ = 13; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + } // { dg-message "static variable is not name-independent" "" { target c++26 } .-2 } + { + extern int _ (int); + extern long _ (long); + extern float _ (float); + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + extern double _ (double); + extern short _ (short); + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + int _ = 2; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + auto [i, _, _] = a; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + auto [i, j, _] = a; // { dg-warning "structured bindings only available with" "" { target c++14_down } } + auto [k, _, l] = a; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + static auto [i, _, _] = a; // { dg-error "redeclaration of 'auto _'" } + // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } + // { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-2 } + } // { dg-message "static structured binding is not name-independent" "" { target c++26 } .-3 } +} + +int +bar (int _ = 0) +{ + int _ = 1; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + return 0; +} + +void +baz () +{ + if (int _ = bar ()) + { + int _ = 6; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + else + { + int _ = 7; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + while (int _ = bar ()) + { + int _ = 8; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + for (int _ = bar (); _; ++_) + { + int _ = 9; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } +} + +namespace A +{ + int _ = 1; + int _ = 1; // { dg-error "redefinition of 'int A::_'" } +} // { dg-message "variable at namespace scope is not name-independent" "" { target c++26 } .-1 } + +namespace B +{ + auto [_, _, _] = a; // { dg-error "redefinition of 'auto B::_'" } + // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } +} // { dg-message "structured binding at namespace scope is not name-independent" "" { target c++26 } .-2 } + +void +qux () +{ + auto c = [_ = 2, _ = 3] () { // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } .-1 } + (void) _; // { dg-error "reference to '_' is ambiguous" } + }; + { + int _ = 4; + auto d = [_, _ = 5] () { // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } .-1 } + (void) _; // { dg-error "reference to '_' is ambiguous" } + }; + } + auto e = [_ = 1] (int _) {}; // { dg-warning "lambda capture initializers only available with" "" { target c++11_down } } +} // { dg-error "lambda parameter '_' previously declared as a capture" "" { target *-*-* } .-1 } + +void +corge (int _, int _) // { dg-error "redefinition of 'int _'" } +{ // { dg-message "parameter declaration is not name-independent" "" { target c++26 } .-1 } +} + +namespace C +{ + typedef int _; + typedef int _; +} + +namespace D +{ + namespace { + int _; + int _; // { dg-error "redefinition of 'int D::.anonymous.::_'" } + } // { dg-message "variable at namespace scope is not name-independent" "" { target c++26 } .-1 } +} + +namespace E +{ + int _ (int); + int _ (int); + int _ (int) { return 0; } + int _ (int) { return 0; } // { dg-error "redefinition of 'int E::_\\\(int\\\)'" } + long _ (long) { return 1; } +} + +template // { dg-error "redefinition of 'int _'" } +void +garply () +{ +} + +#if __cpp_concepts >= 202002L +template +concept F = requires (T _, T _) { T{}; }; // { dg-error "redefinition of 'T _'" "" { target c++20 } } +#endif // { dg-message "parameter declaration is not name-independent" "" { target c++26 } .-1 } --- gcc/testsuite/g++.dg/cpp26/name-independent-decl3.C.jj 2023-11-29 15:51:49.401880196 +0100 +++ gcc/testsuite/g++.dg/cpp26/name-independent-decl3.C 2023-11-29 17:53:12.131669848 +0100 @@ -0,0 +1,12 @@ +// P2169R4 - A nice placeholder with no name +// { dg-do compile { target c++11 } } +// { dg-options "" } + +void +foo () +{ + extern int _; + extern int _; + ++_; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +} --- gcc/testsuite/g++.dg/cpp26/name-independent-decl4.C.jj 2023-11-29 15:51:49.401880196 +0100 +++ gcc/testsuite/g++.dg/cpp26/name-independent-decl4.C 2023-11-29 17:53:17.361595773 +0100 @@ -0,0 +1,12 @@ +// P2169R4 - A nice placeholder with no name +// { dg-do compile { target c++11 } } +// { dg-options "" } + +void +foo () +{ + extern int _; + extern int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } +} --- gcc/testsuite/g++.dg/cpp26/name-independent-decl5.C.jj 2023-11-29 15:51:49.401880196 +0100 +++ gcc/testsuite/g++.dg/cpp26/name-independent-decl5.C 2023-11-29 17:27:10.896655212 +0100 @@ -0,0 +1,92 @@ +// P2169R4 - A nice placeholder with no name +// { dg-do compile { target c++11 } } +// { dg-options "" } + +struct S { + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; +S s = { 1, 2 }; + +struct T { + int _ = 3; + int _ = 4; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; +T t1; +#if __cplusplus >= 201402L +T t2 = { 5, 6 }; +#endif + +struct U { + int _ (int) { return 1; } + long _ (long) { return 2; } + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; +U u = { 7 }; + +struct V { + static int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; +V v = { 8 }; + +struct W : public S, T { int _; }; +struct X : public S, T { + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; + +struct Y { + int _; + int &foo () { return _; } +}; + +struct Z : public Y { + int _; + int bar (); +}; + +int +Z::bar () +{ + return _ + Y::_; +} + +struct A { + int _; + void foo () { + int _; + _ = 42; + _ += ({ int _ = 0; _; }); + } +}; + +struct B { + union { int _; }; + void foo () { ++_; }; +}; + +struct C { + int _; + union { int x; }; + void foo () { ++_; }; +}; + +struct D { + struct { int _; }; + void foo () { ++_; }; +}; + +struct E { + struct _ {}; + int _; + void foo () { ++_; int _; _ = 5; } +}; +typedef struct E::_ E_; + +struct F { + struct _ {}; + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; +typedef struct F::_ F_; --- gcc/testsuite/g++.dg/cpp26/name-independent-decl6.C.jj 2023-11-29 15:51:49.401880196 +0100 +++ gcc/testsuite/g++.dg/cpp26/name-independent-decl6.C 2023-11-29 17:58:42.506990066 +0100 @@ -0,0 +1,135 @@ +// P2169R4 - A nice placeholder with no name +// { dg-do compile { target c++11 } } +// { dg-options "" } + +struct S { + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + int foo (); + S () : _ (1) {} // { dg-error "request for member '_' is ambiguous" } + void bar () { ++_; } // { dg-error "reference to '_' is ambiguous" } +}; + +int +S::foo () +{ + int x = _; // { dg-error "reference to '_' is ambiguous" } + x += S::_; // { dg-error "reference to '_' is ambiguous" } + return x; +} + +struct T { + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; +T t = { ._ = 1 }; // { dg-error "request for member '_' is ambiguous" } + +auto o = __builtin_offsetof (T, _); // { dg-error "request for member '_' is ambiguous" } +int T::* p = &T::_; // { dg-error "reference to '_' is ambiguous" } + +struct U { + U () : _ (42) {} // { dg-error "request for member '_' is ambiguous" } + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; + +struct V { + V (); + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; + +V::V () : _(42) // { dg-error "request for member '_' is ambiguous" } +{ +} + +struct A { + int _; + union { int _; }; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + A() : _(42) {} // { dg-error "request for member '_' is ambiguous" } +}; + +struct B { + union { int _, _; }; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + union { int _, _; }; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + B() : _(42) {} // { dg-error "request for member '_' is ambiguous" } +}; + +void +bar () +{ + union { int _; + int _; }; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + _ = 42; // { dg-error "reference to '_' is ambiguous" } +} + +namespace C +{ + static union { int _ = 1; }; + static union { int _ = 2; }; // { dg-error "redeclaration of 'int _'" } +} + +void +baz () +{ + static union { int _ = 3; }; + static union { int _ = 4; }; // { dg-error "redeclaration of 'int _'" } +} // { dg-message "static variable is not name-independent" "" { target c++26 } .-1 } + +struct D { + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } +}; + +struct E : public D {}; + +void +qux () +{ + D {}._; // { dg-error "request for member '_' is ambiguous" } + E {}._; // { dg-error "request for member '_' is ambiguous" } +} + +struct F { + struct _ {}; + int _; + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + void foo () { ++_; } // { dg-error "reference to '_' is ambiguous" } + void bar (); +}; +typedef struct F::_ F_; + +void +F::bar () +{ + ++_; // { dg-error "reference to '_' is ambiguous" } +} + +struct G { + int _ (int) { return 1; } + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + void foo () { ++_; } // { dg-error "reference to '_' is ambiguous" } + void bar (); +}; + +void +G::bar () +{ + ++_; // { dg-error "reference to '_' is ambiguous" } + this->_ (0); // { dg-error "request for member '_' is ambiguous" } +} + +struct H { + int _ (int) { return 1; } + long _ (float) { return 2; } + int _; // { dg-warning "name-independent declarations only available with" "" { target c++23_down } } + void foo () { ++_; } // { dg-error "reference to '_' is ambiguous" } + void bar (); +}; + +void +H::bar () +{ + ++_; // { dg-error "reference to '_' is ambiguous" } + this->_ (0); // { dg-error "request for member '_' is ambiguous" } +} --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2023-11-24 08:43:01.997951745 +0100 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-11-29 15:51:49.415879997 +0100 @@ -584,7 +584,7 @@ # error "__cpp_auto_cast != 202110" #endif -// C++23 attributes: +// C++23 attributes: #ifdef __has_cpp_attribute # if ! __has_cpp_attribute(assume) @@ -595,3 +595,11 @@ #else # error "__has_cpp_attribute" #endif + +// C++26 features: + +#ifndef __cpp_placeholder_variables +# error "__cpp_placeholder_variables" +#elif __cpp_placeholder_variables != 202306 +# error "__cpp_placeholder_variables != 202306" +#endif