From patchwork Tue Aug 22 07:39:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 136460 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b82d:0:b0:3f2:4152:657d with SMTP id z13csp3465598vqi; Tue, 22 Aug 2023 00:40:46 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFCYSpn2LrtoqppTxtYGle5/g4ad1ZeBvalPGNUUeXeQvGF+tKKOWg0h0Hv+IEUMLxRG83A X-Received: by 2002:a17:906:3d2a:b0:99c:b0c9:4ec0 with SMTP id l10-20020a1709063d2a00b0099cb0c94ec0mr7075135ejf.30.1692690045799; Tue, 22 Aug 2023 00:40:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1692690045; cv=none; d=google.com; s=arc-20160816; b=kn1KGiUXv9B/b9Sll6hkGlYpBm5rt4KDuvxDwNpnOIX7AIC2RYcvrPPRL9a0sAmhb1 nm3N3oL8d+0rzmswHng12dvFAdQ7bz0g2/cZJKanIwgqTOounrtJsk+RCneJKazv3iWj H+TyU4wG72eZ5ClZkPqe/Uvy2eBBzh2XUFZAD1qj2EMA4fWb33YsH3KXntq/zNHVopk/ rGt56RQx3Ql7ia8DqgM+aXeRmxwwPcrQU3F3jYhO5OvqhwjzBc2eQSCVDSkgfVBqHIHr qNubO8G6Uj2EXz+u0BAiGF7FpHepHu93cIDswrrGWiINKdUz41Se0BfrfQ6JGSqU0UnU S5TA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-disposition:mime-version:message-id:subject:cc:to:date :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=4lkMbfMO6OiulJgR48UH3eWWc8F4aw8iXPz3CZ028ZM=; fh=IMTvZBfL0G7+xziJdWPs4IsGafFJ6QqWQkMOdm9kPF0=; b=b2YGo+E4019oOAAQEudaz/9JSM9p+Jh01SLLMB4FHtjBgVUJWXdd0fu29Sf755rUDb 0bMbrjJiR5kL9Ks2zel9oyNt0Vot27AXTZg3lx69Ofr8b5mnbI7ZoURGfkKm8hrJpdRH xWuPD1w8Z+zlBpb9Ncl2ZwoponeGjcGoohhMp2CXCuy7NvwixZ95mhvQGiJU5VLxPanS 60Mq3BeOD/MD33vIagqYeEhThcVfFtG8h/+kZKkYOUyE3r+YM+s2TVjGb/R28qMkBCmO ySkc/OwPi0d7ymxdsugz07bl9kID5cvBtlKxb3VkOvjv4yGCPA3Kkmd1yAJzYt85JrwG 977Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=AWkSmBM+; 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=gnu.org Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id cd13-20020a170906b34d00b0099347270140si6602533ejb.565.2023.08.22.00.40.45 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 22 Aug 2023 00:40:45 -0700 (PDT) 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=@gcc.gnu.org header.s=default header.b=AWkSmBM+; 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=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3C268385696A for ; Tue, 22 Aug 2023 07:40:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3C268385696A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1692690044; bh=4lkMbfMO6OiulJgR48UH3eWWc8F4aw8iXPz3CZ028ZM=; h=Date:To:Cc:Subject:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=AWkSmBM+A/RjlIxXNUOIHeVKvOPVWWoEz7NdD9hIEwQgX5Hjj781F+VI8tDYqvVtt MfMXPS9J6rGDCQhi0O11SsprQLWDjFBMC/EGSkHA2czuQJnNInrK7HHQATDhBuIQTw jfqDLMhMIWxSNQFFG3PVYTyEw2WlZLIS/capknkE= 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.129.124]) by sourceware.org (Postfix) with ESMTPS id 360E23858D28 for ; Tue, 22 Aug 2023 07:39:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 360E23858D28 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-675-qU-vrx26OzWNtAXV3cMJPQ-1; Tue, 22 Aug 2023 03:39:51 -0400 X-MC-Unique: qU-vrx26OzWNtAXV3cMJPQ-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 326EA85D061 for ; Tue, 22 Aug 2023 07:39:51 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.225.165]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8BE72492C13; Tue, 22 Aug 2023 07:39: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 37M7dBAF2415995 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 22 Aug 2023 09:39:35 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 37M7dBpV2415994; Tue, 22 Aug 2023 09:39:11 +0200 Date: Tue, 22 Aug 2023 09:39:11 +0200 To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: Implement C++26 P2169R4 - Placeholder variables with no name [PR110349] Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-3.4 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_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP 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.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jakub Jelinek via Gcc-patches From: Jakub Jelinek Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1774914157514972939 X-GMAIL-MSGID: 1774914157514972939 Hi! 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 As you preferred in the PR, for local scope bindings, the ambiguous cases use a TREE_LIST with the ambiguous cases which can often be directly fed into print_candidates. For member_vec after sorting/deduping, I chose to use instead OVERLOAD with a new flag but only internally inside of the member_vec, get_class_binding_direct turns it into a TREE_LIST. There are 2 reasons for that, 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, and the callers need to be able to chain the results anyway and so need an unshared TREE_LIST they can tweak/destroy anyway. name-independent declarations (even in older standards) will not have -Wunused{,-variable,-but-set-variable} or -Wshadow* warnings diagnosed, but unlike e.g. the clang implementation, this patch does diagnose -Wunused-parameter for parameters with _ names because they aren't name-independent and one can just omit their name instead. 2023-08-22 Jakub Jelinek PR c++/110349 gcc/ * doc/invoke.texi (-Wno-c++26-extensions): Document. gcc/c-family/ * c.opt (Wc++26-extensions): New option. * 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. (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. (placeholder_linear_search): New function. (get_class_binding_direct): Handle member_vec_binary_search returning OVL_PLACEHOLDER_P OVERLOAD. Use placeholder_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. (lookup_name): Likewise. * search.cc (lookup_field_r): Handle nval being a TREE_LIST. * lambda.cc (build_capture_proxy): Adjust for ___. names of members. (add_capture): Add PLACEHOLDER_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. (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/placeholder1.C: New test. * g++.dg/cpp26/placeholder2.C: New test. * g++.dg/cpp26/placeholder3.C: New test. * g++.dg/cpp26/placeholder4.C: New test. * g++.dg/cpp26/placeholder5.C: New test. * g++.dg/cpp26/placeholder6.C: New test. * g++.dg/cpp26/feat-cxx26.C: Add __cpp_placeholder_variables test. Jakub --- gcc/doc/invoke.texi.jj 2023-08-18 09:33:45.748666593 +0200 +++ gcc/doc/invoke.texi 2023-08-21 20:49:48.737668723 +0200 @@ -9014,6 +9014,13 @@ Do not warn about C++23 constructs in co an older C++ standard. Even without this option, some C++23 constructs will only be diagnosed if @option{-Wpedantic} is used. +@opindex Wc++26-extensions +@opindex Wno-c++26-extensions +@item -Wno-c++26-extensions @r{(C++ and Objective-C++ only)} +Do not warn about C++26 constructs in code being compiled using +an older C++ standard. Even without this option, some C++26 constructs +will only be diagnosed if @option{-Wpedantic} is used. + @opindex Wcast-qual @opindex Wno-cast-qual @item -Wcast-qual --- gcc/c-family/c.opt.jj 2023-08-21 11:57:33.002462109 +0200 +++ gcc/c-family/c.opt 2023-08-21 12:07:07.294996931 +0200 @@ -478,6 +478,10 @@ Wc++23-extensions C++ ObjC++ Var(warn_cxx23_extensions) Warning Init(1) Warn about C++23 constructs in code compiled with an older standard. +Wc++26-extensions +C++ ObjC++ Var(warn_cxx26_extensions) Warning Init(1) +Warn about C++26 constructs in code compiled with an older standard. + Wcast-function-type C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra) Warn about casts between incompatible function types. --- gcc/c-family/c-cppbuiltin.cc.jj 2023-08-21 11:57:33.001462122 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2023-08-21 12:07:07.294996931 +0200 @@ -1086,6 +1086,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++26. */ cpp_define (pfile, "__cpp_constexpr=202306L"); + cpp_define (pfile, "__cpp_placeholder_variables=202306L"); } if (flag_concepts) { --- gcc/cp/cp-tree.h.jj 2023-08-21 11:57:33.020461875 +0200 +++ gcc/cp/cp-tree.h 2023-08-21 12:09:46.521927151 +0200 @@ -519,6 +519,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_PLACEHOLDER_P (in OVERLOAD) Usage of TYPE_LANG_FLAG_?: 0: TYPE_DEPENDENT_P @@ -807,6 +808,8 @@ 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_PLACEHOLDER_P(NODE) TREE_LANG_FLAG_6 (OVERLOAD_CHECK (NODE)) /* The first decl of an overload. */ #define OVL_FIRST(NODE) ovl_first (NODE) @@ -7805,7 +7808,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); @@ -8887,6 +8890,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-08-21 11:57:33.089460978 +0200 +++ gcc/cp/name-lookup.cc 2023-08-21 20:31:23.816742992 +0200 @@ -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 +placeholder_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,34 @@ get_class_binding_direct (tree klass, tr val = member_vec_binary_search (member_vec, lookup); if (!val) ; + else if (TREE_CODE (val) == OVERLOAD && OVL_PLACEHOLDER_P (val)) + { + if (want_type) + { + while (TREE_CODE (val) == OVERLOAD && OVL_PLACEHOLDER_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 + { + tree ovl = val; + val = NULL_TREE; + while (TREE_CODE (ovl) == OVERLOAD && OVL_PLACEHOLDER_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 +1947,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 = placeholder_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 +2145,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 +2298,15 @@ member_vec_append_enum_values (vec */ + */ static void member_vec_dedup (vec *member_vec) @@ -2208,6 +2324,7 @@ member_vec_dedup (vec *memb tree to_type = NULL_TREE; tree to_using = NULL_TREE; tree marker = NULL_TREE; + unsigned placeholder = ix; for (jx = ix; jx < len; jx++) { @@ -2251,7 +2368,9 @@ member_vec_dedup (vec *memb continue; } - if (!current) + if (name_independent_decl_p (next)) + placeholder = jx + 1; + else if (!current) current = next; } @@ -2271,6 +2390,17 @@ member_vec_dedup (vec *memb current = stat_hack (current, to_type); } + for (unsigned kx = placeholder; 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_PLACEHOLDER_P (current) = 1; + } + if (current) { if (marker) @@ -2479,10 +2609,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 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 +2726,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 +2833,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, + "placeholder variables 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 +2945,7 @@ update_binding (cp_binding_level *level, tree old_type = NULL_TREE; bool hide_type = false; bool hide_value = false; + bool placeholder_p = false; if (!slot) { @@ -2793,6 +2953,7 @@ update_binding (cp_binding_level *level, hide_type = HIDDEN_TYPE_BINDING_P (binding); if (!old_type) hide_value = hide_type, hide_type = false; + placeholder_p = name_independent_decl_p (decl); } else if (STAT_HACK_P (*slot)) { @@ -2888,7 +3049,9 @@ update_binding (cp_binding_level *level, } else if (old) { - if (TREE_CODE (old) != TREE_CODE (decl)) + if (placeholder_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,17 +3251,17 @@ 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; /* External decls are something else. */ if (DECL_EXTERNAL (decl)) - return; + return NULL_TREE; tree old = NULL_TREE; cp_binding_level *old_scope = NULL; @@ -3135,7 +3298,7 @@ check_local_shadow (tree decl) "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 @@ -3154,11 +3317,13 @@ 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; error_at (DECL_SOURCE_LOCATION (decl), "declaration of %q#D shadows a parameter", decl); inform (DECL_SOURCE_LOCATION (old), "%q#D previously declared here", old); - return; + return NULL_TREE; } } @@ -3170,7 +3335,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 @@ -3182,12 +3347,14 @@ 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; error_at (DECL_SOURCE_LOCATION (decl), "redeclaration of %q#D", decl); 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 (...) @@ -3206,14 +3373,20 @@ check_local_shadow (tree decl) || current_binding_level->level_chain->kind == sk_catch) && in_function_try_handler)) { + if (name_independent_decl_p (decl)) + return old; auto_diagnostic_group d; if (permerror (DECL_SOURCE_LOCATION (decl), "redeclaration of %q#D", decl)) inform (DECL_SOURCE_LOCATION (old), "%q#D previously declared here", old); - return; + return NULL_TREE; } + /* Don't emit -Wshadow* warnings for placeholders. */ + if (name_independent_decl_p (decl)) + 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 @@ -3266,15 +3439,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 placeholders. */ + 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 (), @@ -3302,7 +3479,7 @@ check_local_shadow (tree decl) suppress_warning (decl, OPT_Wshadow); } } - return; + return NULL_TREE; } /* Now look for a namespace shadow. */ @@ -3324,10 +3501,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 @@ -3646,6 +3823,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 placeholder_p = false; + bool placeholder_diagnosed_p = false; if (level->kind == sk_namespace) { @@ -3669,56 +3848,82 @@ pushdecl (tree decl, bool hiding) binding = find_local_binding (level, name); if (binding) old = binding->value; + placeholder_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 (placeholder_p) + { + /* Ignore name-independent declarations. */ + 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; + } + else if (tree match + = duplicate_decls (decl, *iter, hiding, iter.hidden_p ())) { - /* Unhiding a previously hidden decl. */ - tree head = iter.reveal_node (old); - if (head != old) + 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) + { + /* 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)) { - gcc_checking_assert (ns); - if (STAT_HACK_P (*slot)) - STAT_DECL (*slot) = head; + /* 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); + return 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; - } + } /* Check for redeclaring an import. */ if (slot && *slot && TREE_CODE (*slot) == BINDING_VECTOR) @@ -3767,7 +3972,20 @@ pushdecl (tree decl, bool hiding) if (level->kind != sk_namespace) { - 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; + } + } if (TREE_CODE (decl) == NAMESPACE_DECL) /* A local namespace alias. */ @@ -7566,7 +7784,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. */ + name_lookup lookup (name); + for (b = binding; b; b = TREE_CHAIN (b)) + lookup.add_value (TREE_VALUE (b)); + binding = lookup.value; + break; + } + } + } else if (bool (want & LOOK_want::TYPE) && !HIDDEN_TYPE_BINDING_P (iter) && iter->type) --- gcc/cp/search.cc.jj 2023-05-04 12:13:50.847646794 +0200 +++ gcc/cp/search.cc 2023-08-21 13:50:04.622590835 +0200 @@ -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-08-21 11:57:33.076461147 +0200 +++ gcc/cp/lambda.cc 2023-08-21 12:07:07.285997048 +0200 @@ -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 *placeholder_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, "_") && placeholder_cnt) + { + if (*placeholder_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", *placeholder_cnt); + name = get_identifier (buf2); + } + placeholder_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-08-21 11:57:33.043461576 +0200 +++ gcc/cp/decl.cc 2023-08-21 15:41:21.704364443 +0200 @@ -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) @@ -6824,8 +6826,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-08-21 11:57:33.126460497 +0200 +++ gcc/cp/parser.cc 2023-08-21 12:07:07.293996944 +0200 @@ -11279,6 +11279,7 @@ cp_parser_lambda_introducer (cp_parser* hash_set ids; tree first_capture_id = NULL_TREE; + unsigned placeholder_cnt = 0; while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE)) { cp_token* capture_token; @@ -11323,7 +11324,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; } @@ -11345,7 +11346,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; } @@ -11532,13 +11533,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, &placeholder_cnt); /* If there is any qualification still in effect, clear it now; we will be starting fresh with the next capture. */ @@ -15660,6 +15663,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-08-21 11:57:33.156460107 +0200 +++ gcc/cp/pt.cc 2023-08-21 12:07:07.283997074 +0200 @@ -20076,7 +20076,7 @@ tsubst_lambda_expr (tree t, tree args, t && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); vec* field_packs = NULL; - + unsigned placeholder_cnt = 0; for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t); cap; cap = TREE_CHAIN (cap)) { @@ -20106,7 +20106,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), + &placeholder_cnt); continue; } --- gcc/testsuite/g++.dg/cpp26/placeholder1.C.jj 2023-08-21 12:07:07.294996931 +0200 +++ gcc/testsuite/g++.dg/cpp26/placeholder1.C 2023-08-21 20:41:12.890241798 +0200 @@ -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 "placeholder variables only available with" "" { target c++23_down } } + } + { + int _ = 5; + --_; + int _ = 6; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + int _ = 7; // { dg-warning "placeholder variables 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 "placeholder variables 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 "placeholder variables 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 "placeholder variables only available with" "" { target c++23_down } } + } + { + extern int _ (int); + extern long _ (long); + extern float _ (float); + int _ = 1; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + } + { + extern double _ (double); + extern short _ (short); + int _ = 1; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + int _ = 2; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + } + { + int _ = 1; + { + int _ = 2; + ++_; + } + { + static int _ = 3; // { dg-warning "declaration of '_' shadows a previous local" } + ++_; + } + { + 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 "placeholder variables only available with" "" { target c++23_down } } + return 0; +} + +void +baz () +{ + if (int _ = bar ()) + int _ = 2; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + else + int _ = 3; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + while (int _ = bar ()) + int _ = 4; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + for (int _ = bar (); _; ++_) + int _ = 5; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + if (int _ = bar ()) + { + int _ = 6; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + } + else + { + int _ = 7; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + } + while (int _ = bar ()) + { + int _ = 8; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + } + for (int _ = bar (); _; ++_) + { + int _ = 9; // { dg-warning "placeholder variables 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 "placeholder variables 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 "placeholder variables 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 "placeholder variables only available with" "" { target c++23_down } } + } +} + +void +waldo (int _) // { dg-warning "unused parameter '_'" } +try +{ +} +catch (int _) // { dg-warning "placeholder variables only available with" "" { target c++23_down } } +{ + int _ = 7; +} + +void +grault (int _) // { dg-warning "unused parameter '_'" } +try +{ +} +catch (int) +{ + int _ = 8; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } +} + +void +plugh (int _) // { dg-warning "unused parameter '_'" } +try +{ + int _ = 1; +} +catch (int) +{ +} --- gcc/testsuite/g++.dg/cpp26/placeholder2.C.jj 2023-08-21 12:07:07.293996944 +0200 +++ gcc/testsuite/g++.dg/cpp26/placeholder2.C 2023-08-21 20:39:56.297217127 +0200 @@ -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 "placeholder variables 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 _'" } + } + { + int _ = 6; + int _ = 7; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + int _ = 8; + int _ = 9; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + int _ = 10; // { dg-warning "placeholder variables 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 "placeholder variables only available with" "" { target c++23_down } } + } + { + extern int _ (int); + extern long _ (long); + extern float _ (float); + int _ = 1; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + extern double _ (double); + extern short _ (short); + int _ = 1; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + int _ = 2; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + { + auto [i, _, _] = a; // { dg-warning "placeholder variables 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 "placeholder variables 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 } + } +} + +int +bar (int _ = 0) +{ + int _ = 1; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + return 0; +} + +void +baz () +{ + if (int _ = bar ()) + { + int _ = 6; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + else + { + int _ = 7; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + while (int _ = bar ()) + { + int _ = 8; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } + } + for (int _ = bar (); _; ++_) + { + int _ = 9; // { dg-warning "placeholder variables 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::_'" } +} + +namespace B +{ + auto [_, _, _] = a; // { dg-error "redefinition of 'auto B::_'" } + // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } +} + +void +qux () +{ + auto c = [_ = 2, _ = 3] () { // { dg-warning "placeholder variables 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 "placeholder variables 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 _'" } +{ +} + +namespace C +{ + typedef int _; + typedef int _; +} + +namespace D +{ + namespace { + int _; + int _; // { dg-error "redefinition of 'int D::.anonymous.::_'" } + } +} + +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 --- gcc/testsuite/g++.dg/cpp26/placeholder3.C.jj 2023-08-21 12:07:07.293996944 +0200 +++ gcc/testsuite/g++.dg/cpp26/placeholder3.C 2023-08-21 14:28:03.623585509 +0200 @@ -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 "placeholder variables only available with" "" { target c++23_down } } +} --- gcc/testsuite/g++.dg/cpp26/placeholder4.C.jj 2023-08-21 14:26:22.511862429 +0200 +++ gcc/testsuite/g++.dg/cpp26/placeholder4.C 2023-08-21 14:27:49.441764615 +0200 @@ -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 "placeholder variables only available with" "" { target c++23_down } } + ++_; // { dg-error "reference to '_' is ambiguous" } +} --- gcc/testsuite/g++.dg/cpp26/placeholder5.C.jj 2023-08-21 14:27:11.960237949 +0200 +++ gcc/testsuite/g++.dg/cpp26/placeholder5.C 2023-08-21 18:51:46.898745854 +0200 @@ -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 "placeholder variables only available with" "" { target c++23_down } } +}; +S s = { 1, 2 }; + +struct T { + int _ = 3; + int _ = 4; // { dg-warning "placeholder variables 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 "placeholder variables only available with" "" { target c++23_down } } +}; +U u = { 7 }; + +struct V { + static int _; + int _; // { dg-warning "placeholder variables 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 "placeholder variables 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 "placeholder variables only available with" "" { target c++23_down } } +}; +typedef struct F::_ F_; --- gcc/testsuite/g++.dg/cpp26/placeholder6.C.jj 2023-08-21 14:56:33.156776363 +0200 +++ gcc/testsuite/g++.dg/cpp26/placeholder6.C 2023-08-21 18:56:16.909318781 +0200 @@ -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 "placeholder variables 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 "placeholder variables 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 "placeholder variables only available with" "" { target c++23_down } } +}; + +struct V { + V (); + int _; + int _; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } +}; + +V::V () : _(42) // { dg-error "request for member '_' is ambiguous" } +{ +} + +struct A { + int _; + union { int _; }; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + A() : _(42) {} // { dg-error "request for member '_' is ambiguous" } +}; + +struct B { + union { int _, _; }; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + union { int _, _; }; // { dg-warning "placeholder variables only available with" "" { target c++23_down } } + B() : _(42) {} // { dg-error "request for member '_' is ambiguous" } +}; + +void +bar () +{ + union { int _; + int _; }; // { dg-warning "placeholder variables 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 _'" } +} + +struct D { + int _; + int _; // { dg-warning "placeholder variables 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 "placeholder variables 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 "placeholder variables 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 "placeholder variables 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-08-21 11:57:33.193459626 +0200 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-08-21 12:07:07.294996931 +0200 @@ -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