From patchwork Tue Oct 22 19:22:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 99350 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 823623858C62 for ; Tue, 22 Oct 2024 19:23:08 +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.129.124]) by sourceware.org (Postfix) with ESMTP id 4F7C03858D21 for ; Tue, 22 Oct 2024 19:22:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4F7C03858D21 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 4F7C03858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729624961; cv=none; b=PbUpfOIb+vDnHo4oIVt4ONo0Ve1SFI5IOr2WSUe8XHFo9IUoKItP+1QwYYtpNIu3escfWnwCYGeopTw9gQwUz9+hQMMyQocETpmACMB3wPMDCmMX2IRrOTUCeyjqG6475ZOS69uVXTIFKEPyFnYqAVGfScN/4BoYn9R+2ACMHjI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729624961; c=relaxed/simple; bh=8F5/h6cmMFhYjnCFIOIE0d6kmdKcIAe5Vm9qCHjowMw=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ollwdp985+slvDxOYoPq4M8+31OVUp1GLejxx/FWMgADjaEVjkTtXhUW7hEwJHnml9cO13wJ8kHlosMONfoVSfNOjETOBYzY5abll5bjCEi9hG+J359mrCwyYGfrhZcvZXQdVwayQApPVm1UpGwp1gRYEyQqJ0CCpRoB04k4RPQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1729624956; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=nyUpEH38HrfFEidxQxx9h2nUek48BtgyQko1nghCCU8=; b=ItvfVKumYNDH7jGpERpuhv6xF3Q0++/wh1+ErTztjjGWdL+2PHtN0Bw6x2EyQMBe1tR8OX OCunp43kTQjtl7bYrKEsu/pWtzBiIt1tpLkTsmiqEBC2xjo2v5q/UUFKIO/f9J1x8iLfo2 BNC1DtMhq/LRiqITTYviftM+yn7ihxI= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-629-r6q0qp68MDG6dh5qUgqizQ-1; Tue, 22 Oct 2024 15:22:33 -0400 X-MC-Unique: r6q0qp68MDG6dh5qUgqizQ-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BE56819560A2 for ; Tue, 22 Oct 2024 19:22:31 +0000 (UTC) Received: from pdp-11.lan (unknown [10.22.90.14]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 093161955F42; Tue, 22 Oct 2024 19:22:30 +0000 (UTC) From: Marek Polacek To: GCC Patches , Jason Merrill Subject: [PATCH] c++: Implement P2662R3, Pack Indexing [PR113798] Date: Tue, 22 Oct 2024 15:22:17 -0400 Message-ID: <20241022192217.508500-1-polacek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- This patch implements C++26 Pack Indexing, as described in . The issue discussing how to mangle pack indexes has not been resolved yet and I've made no attempt to address it so far. Rather than introducing a new template code for a pack indexing, I'm adding a new operand to EXPR_PACK_EXPANSION to store the index; for TYPE_PACK_EXPANSION, I'm stashing the index into TYPE_VALUES_RAW. This feature is akin to __type_pack_element, so they can share the element extraction part. A pack indexing in a decltype proved to be a bit tricky; eventually, I've added PACK_EXPANSION_PARENTHESIZED_P -- while parsing, we can't really tell what it's going to expand to. With this feature, it's valid to write something like using U = tmpl; where we first expand the template argument into Ts...[Is#0], Ts...[Is#1], ... and then substitute each individual pack index. I have no test for the module.cc code, that is just guesswork. PR c++/113798 gcc/cp/ChangeLog: * cp-tree.def (EXPR_PACK_EXPANSION): Add another operand. * cp-tree.h (PACK_EXPANSION_INDEX): Define. (PACK_EXPANSION_PARENTHESIZED_P): Define. (pack_index_element): Declare. * cxx-pretty-print.cc (cxx_pretty_printer::expression) : Print PACK_EXPANSION_INDEX. (cxx_pretty_printer::type_id) : Print PACK_EXPANSION_INDEX. * decl.cc (xref_basetypes): Set PACK_EXPANSION_INDEX. * error.cc (dump_type): Print PACK_EXPANSION_INDEX. * mangle.cc (write_type) : New comment. * module.cc (trees_out::type_node): Stream PACK_EXPANSION_INDEX. (trees_in::tree_node): Read in PACK_EXPANSION_INDEX. * parser.cc (cp_parser_pack_index): New. (cp_parser_primary_expression): Handle a pack-index-expression. (cp_parser_unqualified_id): Handle a pack-index-specifier. (cp_parser_nested_name_specifier_opt): Also check for a pack-index-specifier. Handle a pack-index-specifier. (cp_parser_mem_initializer_id): Handle a pack-index-specifier. (cp_parser_simple_type_specifier): Likewise. (cp_parser_base_specifier): Likewise. * pt.cc (iterative_hash_template_arg) : Also hash PACK_EXPANSION_INDEX. (find_parameter_packs_r) : A type with PACK_EXPANSION_INDEX is not a bare parameter pack. : Walk into PACK_EXPANSION_INDEXes. (instantiate_class_template): Handle a pack-index-specifier. (tsubst_pack_expansion): tsubst_expr the PACK_EXPANSION_INDEX. If there was a PACK_EXPANSION_INDEX, pull out the element via pack_index_element. (tsubst) : Handle a pack-index-specifier. : For a PACK_EXPANSION_P, figure out if it should be treated as an id-expression. : Handle it if there is a PACK_EXPANSION_INDEX. (tsubst_stmt) : Likewise. (tsubst_expr) : Likewise. (tsubst_initializer_list): Handle a pack-index-specifier. * ptree.cc (cxx_print_type) : Print the PACK_EXPANSION_INDEX. * semantics.cc (finish_parenthesized_expr): Maybe set PACK_EXPANSION_PARENTHESIZED_P. (finish_base_specifier): Check for a PACK_EXPANSION_P with a PACK_EXPANSION_INDEX. (get_vec_elt_checking): New, broken out of finish_type_pack_element. (finish_type_pack_element): Call get_vec_elt_checking. (pack_index_element): New. * tree.cc (cp_build_qualified_type): Set PACK_EXPANSION_INDEX. (cp_tree_equal) : Also compare the PACK_EXPANSION_INDEXes. (cp_walk_subtrees) : Walk the PACK_EXPANSION_INDEX. * typeck.cc (structural_comptypes) : Also compare the PACK_EXPANSION_INDEXes. gcc/testsuite/ChangeLog: * g++.dg/cpp26/pack-indexing1.C: New test. * g++.dg/cpp26/pack-indexing2.C: New test. * g++.dg/cpp26/pack-indexing3.C: New test. * g++.dg/cpp26/pack-indexing4.C: New test. * g++.dg/cpp26/pack-indexing5.C: New test. --- gcc/cp/cp-tree.def | 2 +- gcc/cp/cp-tree.h | 13 ++ gcc/cp/cxx-pretty-print.cc | 12 ++ gcc/cp/decl.cc | 8 +- gcc/cp/error.cc | 6 + gcc/cp/mangle.cc | 2 + gcc/cp/module.cc | 3 + gcc/cp/parser.cc | 144 +++++++++++++++++--- gcc/cp/pt.cc | 90 +++++++++--- gcc/cp/ptree.cc | 1 + gcc/cp/semantics.cc | 50 ++++++- gcc/cp/tree.cc | 14 +- gcc/cp/typeck.cc | 4 +- gcc/testsuite/g++.dg/cpp26/pack-indexing1.C | 102 ++++++++++++++ gcc/testsuite/g++.dg/cpp26/pack-indexing2.C | 111 +++++++++++++++ gcc/testsuite/g++.dg/cpp26/pack-indexing3.C | 41 ++++++ gcc/testsuite/g++.dg/cpp26/pack-indexing4.C | 65 +++++++++ gcc/testsuite/g++.dg/cpp26/pack-indexing5.C | 41 ++++++ 18 files changed, 662 insertions(+), 47 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing1.C create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing2.C create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing3.C create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing4.C create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing5.C base-commit: 5fd1c0c1b6968d55e3f997d67a4c149edf20c012 diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 18f75108c7b..576765667d0 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -395,7 +395,7 @@ DEFTREECODE (TYPE_PACK_EXPANSION, "type_pack_expansion", tcc_type, 0) EXPR_PACK_EXPANSION plays precisely the same role as TYPE_PACK_EXPANSION, but will be used for expressions. */ -DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 3) +DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 4) /* Selects the Ith parameter out of an argument pack. This node will be used when instantiating pack expansions; see diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a44100a2bc4..12472d95247 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -510,6 +510,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; OVL_LOOKUP_P (in OVERLOAD) LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL) FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL) + PACK_EXPANSION_PARENTHESIZED_P (in *_PACK_EXPANSION) 5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE) FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE) CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR) @@ -4025,6 +4026,12 @@ struct GTY(()) lang_decl { ? &TYPE_MAX_VALUE_RAW (NODE) \ : &TREE_OPERAND ((NODE), 2)) +/* For a pack-index T...[N], the index N. */ +#define PACK_EXPANSION_INDEX(NODE) \ + *(TREE_CODE (PACK_EXPANSION_CHECK (NODE)) == TYPE_PACK_EXPANSION \ + ? &TYPE_VALUES_RAW (NODE) \ + : &TREE_OPERAND ((NODE), 3)) + /* True iff this pack expansion is within a function context. */ #define PACK_EXPANSION_LOCAL_P(NODE) \ TREE_LANG_FLAG_0 (PACK_EXPANSION_CHECK (NODE)) @@ -4042,6 +4049,11 @@ struct GTY(()) lang_decl { #define PACK_EXPANSION_FORCE_EXTRA_ARGS_P(NODE) \ TREE_LANG_FLAG_3 (PACK_EXPANSION_CHECK (NODE)) +/* Indicates whether a pack expansion has been parenthesized. Used for + a pack expansion in a decltype. */ +#define PACK_EXPANSION_PARENTHESIZED_P(NODE) \ + TREE_LANG_FLAG_4 (PACK_EXPANSION_CHECK (NODE)) + /* True iff the wildcard can match a template parameter pack. */ #define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE) @@ -7906,6 +7918,7 @@ extern tree finish_underlying_type (tree); extern tree calculate_bases (tree, tsubst_flags_t); extern tree finish_bases (tree, bool); extern tree calculate_direct_bases (tree, tsubst_flags_t); +extern tree pack_index_element (tree, tree, tsubst_flags_t); extern tree finish_offsetof (tree, tree, location_t); extern void finish_decl_cleanup (tree, tree); extern void finish_eh_cleanup (tree); diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index 41e6bdfdda5..47111195f72 100644 --- a/gcc/cp/cxx-pretty-print.cc +++ b/gcc/cp/cxx-pretty-print.cc @@ -1216,6 +1216,12 @@ cxx_pretty_printer::expression (tree t) case EXPR_PACK_EXPANSION: expression (PACK_EXPANSION_PATTERN (t)); pp_cxx_ws_string (this, "..."); + if (PACK_EXPANSION_INDEX (t)) + { + pp_cxx_left_bracket (this); + expression (PACK_EXPANSION_INDEX (t)); + pp_cxx_right_bracket (this); + } break; case UNARY_LEFT_FOLD_EXPR: @@ -1916,6 +1922,12 @@ cxx_pretty_printer::type_id (tree t) case TYPE_PACK_EXPANSION: type_id (PACK_EXPANSION_PATTERN (t)); pp_cxx_ws_string (this, "..."); + if (PACK_EXPANSION_INDEX (t)) + { + pp_cxx_left_bracket (this); + expression (PACK_EXPANSION_INDEX (t)); + pp_cxx_right_bracket (this); + } break; case TYPE_ARGUMENT_PACK: diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 7281818be8f..311f4ad6cd1 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -17040,8 +17040,12 @@ xref_basetypes (tree ref, tree base_list) } if (PACK_EXPANSION_P (TREE_VALUE (base_list))) - /* Regenerate the pack expansion for the bases. */ - basetype = make_pack_expansion (basetype); + { + /* Regenerate the pack expansion for the bases. */ + basetype = make_pack_expansion (basetype); + PACK_EXPANSION_INDEX (basetype) + = PACK_EXPANSION_INDEX (TREE_VALUE (base_list)); + } TYPE_MARKED_P (basetype) = 1; diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index 65f70c595cf..33f964a11bd 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -818,6 +818,12 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) case TYPE_PACK_EXPANSION: dump_type (pp, PACK_EXPANSION_PATTERN (t), flags); pp_cxx_ws_string (pp, "..."); + if (PACK_EXPANSION_INDEX (t)) + { + pp_cxx_left_bracket (pp); + dump_expr (pp, PACK_EXPANSION_INDEX (t), flags & ~TFF_EXPR_IN_PARENS); + pp_cxx_right_bracket (pp); + } break; case TYPE_ARGUMENT_PACK: diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 17988d69e1e..8c8a89897c9 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -2605,6 +2605,8 @@ write_type (tree type) case TYPE_PACK_EXPANSION: write_string ("Dp"); write_type (PACK_EXPANSION_PATTERN (type)); + /* TODO: Mangle PACK_EXPANSION_INDEX + */ break; case DECLTYPE_TYPE: diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index fd9b1d3bf2e..da3e2b71a66 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -9207,6 +9207,7 @@ trees_out::type_node (tree type) u (PACK_EXPANSION_LOCAL_P (type)); tree_node (PACK_EXPANSION_PARAMETER_PACKS (type)); tree_node (PACK_EXPANSION_EXTRA_ARGS (type)); + tree_node (PACK_EXPANSION_INDEX (type)); break; case TYPENAME_TYPE: @@ -9763,6 +9764,7 @@ trees_in::tree_node (bool is_use) bool local = u (); tree param_packs = tree_node (); tree extra_args = tree_node (); + tree index = tree_node (); if (!get_overrun ()) { tree expn = cxx_make_type (TYPE_PACK_EXPANSION); @@ -9771,6 +9773,7 @@ trees_in::tree_node (bool is_use) PACK_EXPANSION_PARAMETER_PACKS (expn) = param_packs; PACK_EXPANSION_EXTRA_ARGS (expn) = extra_args; PACK_EXPANSION_LOCAL_P (expn) = local; + PACK_EXPANSION_INDEX (expn) = index; res = expn; } } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 9d31a975dcf..7a054fb6c05 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -5739,6 +5739,56 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1) return finish_binary_fold_expr (loc, expr1, expr2, op); } +/* Parse a pack-index-specifier: + + pack-index-specifier: + typedef-name ... [ constant-expression ] + + or a pack-index-expression: + + pack-index-expression: + id-expression ... [ constant-expression ] + + PACK is the parsed typedef-name or the id-expression. Returns + either an EXPR_PACK_EXPANSION or TYPE_PACK_EXPANSION. */ + +static tree +cp_parser_pack_index (cp_parser *parser, tree pack) +{ + if (cxx_dialect < cxx26) + pedwarn (cp_lexer_peek_token (parser->lexer)->location, + OPT_Wc__26_extensions, "pack indexing only available with " + "%<-std=c++2c%> or %<-std=gnu++2c%>"); + /* Consume the '...' token. */ + cp_lexer_consume_token (parser->lexer); + /* Consume the '['. */ + cp_lexer_consume_token (parser->lexer); + + if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) + { + error_at (cp_lexer_peek_token (parser->lexer)->location, + "pack index missing"); + cp_lexer_consume_token (parser->lexer); + return error_mark_node; + } + + tree expr = cp_parser_constant_expression (parser, + /*allow_non_constant_p=*/false, + /*non_constant_p=*/nullptr, + /*strict_p=*/true); + /* Consume the ']'. */ + cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); + + if (TREE_CODE (pack) == TYPE_DECL) + pack = TREE_TYPE (pack); + pack = make_pack_expansion (pack); + if (pack == error_mark_node) + return error_mark_node; + PACK_EXPANSION_INDEX (pack) = expr; + + return pack; +} + /* Parse a primary-expression. primary-expression: @@ -6368,6 +6418,12 @@ cp_parser_primary_expression (cp_parser *parser, = make_location (caret_loc, start_loc, finish_loc); decl.set_location (combined_loc); + + /* "T...[constant-expression]" is a C++26 pack-index-expression. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) + decl = cp_parser_pack_index (parser, decl); + return decl; } @@ -6411,6 +6467,7 @@ missing_template_diag (location_t loc, diagnostic_t diag_kind = DK_WARNING) id-expression: unqualified-id qualified-id + pack-index-expression qualified-id: :: [opt] nested-name-specifier template [opt] unqualified-id @@ -6593,7 +6650,9 @@ cp_parser_id_expression (cp_parser *parser, identifier operator-function-id conversion-function-id - ~ class-name + literal-operator-id + ~ type-name + ~ computed-type-specifier template-id If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template' @@ -6900,6 +6959,14 @@ cp_parser_unqualified_id (cp_parser* parser, "typedef-name %qD used as destructor declarator", type_decl); + /* "~T...[N]" is a C++26 pack-index-specifier. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) + { + type_decl = cp_parser_pack_index (parser, type_decl); + return build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl); + } + return build_min_nt_loc (loc, BIT_NOT_EXPR, TREE_TYPE (type_decl)); } @@ -6970,9 +7037,10 @@ check_template_keyword_in_nested_name_spec (tree name) class-or-namespace-name :: nested-name-specifier [opt] class-or-namespace-name :: template nested-name-specifier [opt] - nested-name-specifier: [C++0x] + nested-name-specifier: [C++11] type-name :: namespace-name :: + computed-type-specifier :: nested-name-specifier identifier :: nested-name-specifier template [opt] simple-template-id :: @@ -7080,6 +7148,10 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, } if (token->type != CPP_SCOPE + /* See if a pack-index-specifier follows. */ + && !(token->type == CPP_ELLIPSIS + && cp_lexer_peek_nth_token (parser->lexer, 3)->type + == CPP_OPEN_SQUARE) && !cp_parser_nth_token_starts_template_argument_list_p (parser, 2)) break; @@ -7127,6 +7199,12 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, check_dependency_p, type_p, is_declaration); + + /* "T...[constant-expression]" is a C++26 pack-index-specifier. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) + new_scope = cp_parser_pack_index (parser, new_scope); + /* Look for the `::' token. */ cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); @@ -17938,13 +18016,17 @@ cp_parser_mem_initializer (cp_parser* parser) /* Parse a mem-initializer-id. mem-initializer-id: - :: [opt] nested-name-specifier [opt] class-name - decltype-specifier (C++11) + class-or-decltype identifier + class-or-decltype: + nested-name-specifier [opt] type-name + nested-name-specifier template simple-template-id + computed-type-specifier + Returns a TYPE indicating the class to be initialized for the first - production (and the second in C++11). Returns an IDENTIFIER_NODE - indicating the data member to be initialized for the last production. */ + production. Returns an IDENTIFIER_NODE indicating the data member to + be initialized for the second production. */ static tree cp_parser_mem_initializer_id (cp_parser* parser) @@ -18015,10 +18097,16 @@ cp_parser_mem_initializer_id (cp_parser* parser) /*class_head_p=*/false, /*is_declaration=*/true); /* If we found one, we're done. */ - if (cp_parser_parse_definitely (parser)) - return id; - /* Otherwise, look for an ordinary identifier. */ - return cp_parser_identifier (parser); + if (!cp_parser_parse_definitely (parser)) + /* Otherwise, look for an ordinary identifier. */ + id = cp_parser_identifier (parser); + + /* ": T...[N]" is a C++26 pack-index-specifier. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) + id = cp_parser_pack_index (parser, id); + + return id; } /* Overloading [gram.over] */ @@ -20436,11 +20524,11 @@ cp_parser_type_specifier (cp_parser* parser, C++11 Extension: simple-type-specifier: - auto - decltype ( expression ) char16_t char32_t __underlying_type ( type-id ) + computed-type-specifier + placeholder-type-specifier C++17 extension: @@ -20822,6 +20910,13 @@ cp_parser_simple_type_specifier (cp_parser* parser, type = NULL_TREE; } + /* "T...[constant-expression]" is a C++26 pack-index-specifier. */ + if (type + && type != error_mark_node + && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) + type = cp_parser_pack_index (parser, type); + if (!type && flag_concepts && decl_specs) { /* Try for a type-constraint with template arguments. We check @@ -29103,12 +29198,21 @@ cp_parser_base_clause (cp_parser* parser) /* Parse a base-specifier. base-specifier: - attribute-specifier-seq [opt] :: [opt] nested-name-specifier [opt] - class-name - attribute-specifier-seq [opt] virtual access-specifier [opt] :: [opt] - nested-name-specifier [opt] class-name - attribute-specifier-seq [opt] access-specifier virtual [opt] :: [opt] - nested-name-specifier [opt] class-name + attribute-specifier-seq [opt] class-or-decltype + attribute-specifier-seq [opt] virtual access-specifier [opt] + class-or-decltype + attribute-specifier-seq [opt] access-specifier virtual [opt] + class-or-decltype + + class-or-decltype: + nested-name-specifier [opt] type-name + nested-name-specifier template simple-template-id + computed-type-specifier + + access-specifier: + private + protected + public Returns a TREE_LIST. The TREE_PURPOSE will be one of ACCESS_{DEFAULT,PUBLIC,PROTECTED,PRIVATE}_[VIRTUAL]_NODE to @@ -29237,6 +29341,10 @@ cp_parser_base_specifier (cp_parser* parser) /*class_head_p=*/false, /*is_declaration=*/true); type = TREE_TYPE (type); + /* ": T...[constant-expression]" is a C++26 pack-index-specifier. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) + type = cp_parser_pack_index (parser, type); } if (type == error_mark_node) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index b590c32345f..e7f3ce38348 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -1786,7 +1786,8 @@ iterative_hash_template_arg (tree arg, hashval_t val) case TYPE_PACK_EXPANSION: case EXPR_PACK_EXPANSION: val = iterative_hash_template_arg (PACK_EXPANSION_PATTERN (arg), val); - return iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), val); + val = iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), val); + return iterative_hash_template_arg (PACK_EXPANSION_INDEX (arg), val); case TYPE_ARGUMENT_PACK: case NONTYPE_ARGUMENT_PACK: @@ -3952,7 +3953,11 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) break; case VAR_DECL: - if (DECL_PACK_P (t)) + /* We can have + T...[0] a; + (T...[0])(a); // #1 + where the 'a' in #1 is not a bare parameter pack. */ + if (DECL_PACK_P (t) && !PACK_EXPANSION_INDEX (TREE_TYPE (t))) { /* We don't want to walk into the type of a variadic capture proxy, because we don't want to see the type parameter pack. */ @@ -4027,6 +4032,10 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) case TYPE_PACK_EXPANSION: case EXPR_PACK_EXPANSION: + /* We can have an expansion of an expansion, such as "Ts...[Is]...", + so do look into the index. */ + cp_walk_tree (&PACK_EXPANSION_INDEX (t), &find_parameter_packs_r, ppd, + ppd->visited); *walk_subtrees = 0; return NULL_TREE; @@ -12524,11 +12533,19 @@ instantiate_class_template (tree type) if (PACK_EXPANSION_P (BINFO_TYPE (pbase_binfo))) { - expanded_bases = + expanded_bases = tsubst_pack_expansion (BINFO_TYPE (pbase_binfo), args, tf_error, NULL_TREE); if (expanded_bases == error_mark_node) continue; + /* If there was a pack-index-specifier, we won't get + a TREE_VEC but the rest of the code assumes so. */ + if (PACK_EXPANSION_INDEX (BINFO_TYPE (pbase_binfo))) + { + tree vec = make_tree_vec (1); + TREE_VEC_ELT (vec, 0) = expanded_bases; + expanded_bases = vec; + } len = TREE_VEC_LENGTH (expanded_bases); } @@ -13669,6 +13686,8 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, levels = TMPL_ARGS_DEPTH (args); + tree index = tsubst_expr (PACK_EXPANSION_INDEX (t), args, complain, in_decl); + /* Determine the argument packs that will instantiate the parameter packs used in the expansion expression. While we're at it, compute the number of arguments to be expanded and make sure it @@ -13814,6 +13833,10 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, { tree args = ARGUMENT_PACK_ARGS (TREE_VALUE (packs)); + /* C++26 Pack Indexing. */ + if (index) + return pack_index_element (index, args, complain); + /* If the argument pack is a single pack expansion, pull it out. */ if (TREE_VEC_LENGTH (args) == 1 && pack_expansion_args_count (args)) @@ -13946,6 +13969,10 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, && PACK_EXPANSION_P (TREE_VEC_ELT (result, 0))) return TREE_VEC_ELT (result, 0); + /* C++26 Pack Indexing. */ + if (index) + return pack_index_element (index, result, complain); + return result; } @@ -16897,17 +16924,23 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) ctx = tsubst_pack_expansion (ctx, args, complain | tf_qualifying_scope, in_decl); - if (ctx == error_mark_node - || TREE_VEC_LENGTH (ctx) > 1) + if (ctx == error_mark_node) return error_mark_node; - if (TREE_VEC_LENGTH (ctx) == 0) + /* If there was a pack-index-specifier, we won't get a TREE_VEC, + just the single element. */ + if (TREE_CODE (ctx) == TREE_VEC) { - if (complain & tf_error) - error ("%qD is instantiated for an empty pack", - TYPENAME_TYPE_FULLNAME (t)); - return error_mark_node; + if (TREE_VEC_LENGTH (ctx) > 1) + return error_mark_node; + if (TREE_VEC_LENGTH (ctx) == 0) + { + if (complain & tf_error) + error ("%qD is instantiated for an empty pack", + TYPENAME_TYPE_FULLNAME (t)); + return error_mark_node; + } + ctx = TREE_VEC_ELT (ctx, 0); } - ctx = TREE_VEC_ELT (ctx, 0); } else ctx = tsubst_entering_scope (ctx, args, @@ -17041,13 +17074,20 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) else { bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t); - if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR - && EXPR_P (type)) + tree op = DECLTYPE_TYPE_EXPR (t); + if (id && TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type)) /* In a template ~id could be either a complement expression or an unqualified-id naming a destructor; if instantiating it produces an expression, it's not an id-expression or member access. */ id = false; + /* With pack indexing, we don't know what it's going to expand to + until instantiation. The intent is that a pack indexing + expression behaves exactly as the underlying expression + would. */ + else if (PACK_EXPANSION_P (op)) + id = (!PACK_EXPANSION_PARENTHESIZED_P (op) + && unparenthesized_id_or_class_member_access_p (type)); type = finish_decltype_type (type, id, complain); } return cp_build_qualified_type (type, @@ -17074,6 +17114,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) case NONTYPE_ARGUMENT_PACK: return tsubst_argument_pack (t, args, complain, in_decl); + case TYPE_PACK_EXPANSION: + if (PACK_EXPANSION_INDEX (t)) + return tsubst_pack_expansion (t, args, complain, in_decl); + gcc_fallthrough (); + case VOID_CST: case INTEGER_CST: case REAL_CST: @@ -19596,6 +19641,8 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) } case EXPR_PACK_EXPANSION: + if (PACK_EXPANSION_INDEX (t)) + RETURN (tsubst_pack_expansion (t, args, complain, in_decl)); error ("invalid use of pack expansion expression"); RETURN (error_mark_node); @@ -21776,6 +21823,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) } case EXPR_PACK_EXPANSION: + if (PACK_EXPANSION_INDEX (t)) + RETURN (tsubst_pack_expansion (t, args, complain, in_decl)); error ("invalid use of pack expansion expression"); RETURN (error_mark_node); @@ -27685,6 +27734,14 @@ tsubst_initializer_list (tree t, tree argvec) NULL_TREE); if (expanded_bases == error_mark_node) continue; + /* If there was a pack-index-specifier, we won't get + a TREE_VEC but the rest of the code assumes so. */ + if (PACK_EXPANSION_INDEX (TREE_PURPOSE (t))) + { + tree vec = make_tree_vec (1); + TREE_VEC_ELT (vec, 0) = expanded_bases; + expanded_bases = vec; + } /* We'll be building separate TREE_LISTs of arguments for each base. */ @@ -28115,7 +28172,7 @@ dependent_type_p_r (tree type) } /* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must - be template parameters. */ + be template parameters. This includes pack-index-specifiers. */ if (TREE_CODE (type) == TYPE_PACK_EXPANSION) return true; @@ -28739,7 +28796,8 @@ type_dependent_expression_p (tree expression) && uses_outer_template_parms_in_constraints (expression)) return true; - /* Always dependent, on the number of arguments if nothing else. */ + /* Always dependent, on the number of arguments if nothing else. This + includes pack-index-expressions. */ if (TREE_CODE (expression) == EXPR_PACK_EXPANSION) return true; @@ -31166,7 +31224,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, tree outer_targs, /* Return true if INIT is an unparenthesized id-expression or an unparenthesized class member access. Used for the argument of - decltype(auto). */ + decltype(auto), or for C++26 pack indexing. */ bool unparenthesized_id_or_class_member_access_p (tree init) diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc index 15e46752d01..82d08a31b22 100644 --- a/gcc/cp/ptree.cc +++ b/gcc/cp/ptree.cc @@ -190,6 +190,7 @@ cxx_print_type (FILE *file, tree node, int indent) case TYPE_PACK_EXPANSION: print_node (file, "pattern", PACK_EXPANSION_PATTERN (node), indent + 4); print_node (file, "args", PACK_EXPANSION_EXTRA_ARGS (node), indent + 4); + print_node (file, "index", PACK_EXPANSION_INDEX (node), indent + 4); return; default: diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index dabfb1ff53f..7ccd678252c 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -2452,6 +2452,8 @@ finish_parenthesized_expr (cp_expr expr) tree stripped_expr = tree_strip_any_location_wrapper (expr); if (TREE_CODE (stripped_expr) == STRING_CST) PAREN_STRING_LITERAL_P (stripped_expr) = 1; + else if (PACK_EXPANSION_P (stripped_expr)) + PACK_EXPANSION_PARENTHESIZED_P (stripped_expr) = true; expr = cp_expr (force_paren_expr (expr), expr.get_location ()); @@ -4181,7 +4183,9 @@ finish_base_specifier (tree base, tree access, bool virtual_p) error ("invalid base-class specification"); result = NULL_TREE; } - else if (! MAYBE_CLASS_TYPE_P (base)) + else if (! MAYBE_CLASS_TYPE_P (base) + && ! (PACK_EXPANSION_P (base) + && PACK_EXPANSION_INDEX (base))) { error ("%qT is not a class type", base); result = NULL_TREE; @@ -4837,34 +4841,66 @@ finish_underlying_type (tree type) return underlying_type; } -/* Implement the __type_pack_element keyword: Return the type - at index IDX within TYPES. */ +/* Return the type at index IDX within TYPES. */ static tree -finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain) +get_vec_elt_checking (tree idx, tree types, bool pack_index_p, + tsubst_flags_t complain) { idx = maybe_constant_value (idx); if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx))) { if (complain & tf_error) - error ("%<__type_pack_element%> index is not an integral constant"); + { + if (pack_index_p) + error ("pack index is not an integral constant"); + else + error ("%<__type_pack_element%> index is not an integral constant"); + } return error_mark_node; } if (tree_int_cst_sgn (idx) < 0) { if (complain & tf_error) - error ("%<__type_pack_element%> index is negative"); + { + if (pack_index_p) + error ("pack index is negative"); + else + error ("%<__type_pack_element%> index is negative"); + } return error_mark_node; } if (wi::to_widest (idx) >= TREE_VEC_LENGTH (types)) { if (complain & tf_error) - error ("%<__type_pack_element%> index is out of range"); + { + if (pack_index_p) + error ("pack index is out of range"); + else + error ("%<__type_pack_element%> index is out of range"); + } return error_mark_node; } return TREE_VEC_ELT (types, tree_to_shwi (idx)); } +/* Implement the __type_pack_element keyword: Return the type + at index IDX within TYPES. */ + +static tree +finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain) +{ + return get_vec_elt_checking (idx, types, /*pack_index_p=*/false, complain); +} + +/* In a pack-index T...[N], return the element at index IDX within TYPES. */ + +tree +pack_index_element (tree idx, tree types, tsubst_flags_t complain) +{ + return get_vec_elt_checking (idx, types, /*pack_index_p=*/true, complain); +} + /* Implement the __direct_bases keyword: Return the direct base classes of type. */ diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index c80ee068958..8637ab06d77 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -1441,7 +1441,10 @@ cp_build_qualified_type (tree type, int type_quals, tree t = PACK_EXPANSION_PATTERN (type); t = cp_build_qualified_type (t, type_quals, complain); - return make_pack_expansion (t, complain); + /* Regenerate the pack expansion with a qualified type. */ + t = make_pack_expansion (t, complain); + PACK_EXPANSION_INDEX (t) = PACK_EXPANSION_INDEX (type); + return t; } /* A reference or method type shall not be cv-qualified. @@ -4242,6 +4245,9 @@ cp_tree_equal (tree t1, tree t2) if (!comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1), PACK_EXPANSION_EXTRA_ARGS (t2))) return false; + if (!cp_tree_equal (PACK_EXPANSION_INDEX (t1), + PACK_EXPANSION_INDEX (t2))) + return false; return true; case COMPONENT_REF: @@ -5575,12 +5581,16 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, case TYPE_PACK_EXPANSION: WALK_SUBTREE (TREE_TYPE (t)); WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t)); + if (PACK_EXPANSION_INDEX (t)) + WALK_SUBTREE (PACK_EXPANSION_INDEX (t)); *walk_subtrees_p = 0; break; - + case EXPR_PACK_EXPANSION: WALK_SUBTREE (TREE_OPERAND (t, 0)); WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t)); + if (PACK_EXPANSION_INDEX (t)) + WALK_SUBTREE (PACK_EXPANSION_INDEX (t)); *walk_subtrees_p = 0; break; diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 71d879abef1..4f0d1bc8af2 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -1620,7 +1620,9 @@ structural_comptypes (tree t1, tree t2, int strict) return (same_type_p (PACK_EXPANSION_PATTERN (t1), PACK_EXPANSION_PATTERN (t2)) && comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1), - PACK_EXPANSION_EXTRA_ARGS (t2))); + PACK_EXPANSION_EXTRA_ARGS (t2)) + && cp_tree_equal (PACK_EXPANSION_INDEX (t1), + PACK_EXPANSION_INDEX (t2))); case DECLTYPE_TYPE: if (DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t1) diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C new file mode 100644 index 00000000000..9d72c6582af --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C @@ -0,0 +1,102 @@ +// P2662R3 - Pack Indexing +// PR c++/113798 +// { dg-do compile { target c++17 } } +// { dg-options "" } + +template struct same_type; +template struct same_type {}; + +template +using Type = Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + +template +constexpr auto Var = Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + +template +decltype(Ts...[I]) // { dg-warning "pack indexing only available with" "" { target c++23_down } } +foo () +{ + return Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } } +} + +template +struct S { + Ts...[0] a; // { dg-warning "pack indexing only available with" "" { target c++23_down } } +#if __cpp_concepts >= 201907L + void foo (auto... Vs) { + decltype(Vs...[1]) d1 = Vs...[1]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } } + } +#endif +}; + +int +g () +{ + using U = Type<1, char, int, float>; + using U = int; + + constexpr auto V = Var<2, 0, 1, 42>; + static_assert (V == 42); + + U r = foo<2, 0, 1, 42>(); + + return r; +} + +void +fn1 () +{ + int i = 0; + [&i](auto... pack) { + // type is int + decltype(pack...[0]) x5 = 42; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + // type is int& + [[maybe_unused]] decltype((pack...[0])) x6 = i; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + }(0); +} + +#if __cpp_concepts >= 201907L +int +bar (auto... pack) +{ + (void) pack...[0]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } } + int x = pack...[0]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } } + return x; +} +#endif + +template +void +fn2 () +{ + [[maybe_unused]] decltype(pack...[0]) x1; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + [[maybe_unused]] decltype((pack...[0])) x2; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + same_type(); + same_type(); +} + +template +void +fn3 (int p) +{ + T...[0] a = p; // { dg-warning "pack indexing only available with" "" { target c++23_down } } + (T...[0])(a); // { dg-warning "pack indexing only available with" "" { target c++23_down } } +} + +template +void fn4 () +{ + same_type(); // { dg-warning "pack indexing only available with" "" { target c++23_down } } + same_type(); // { dg-warning "pack indexing only available with" "" { target c++23_down } } +} + +void +g3 () +{ + fn2<0>(); +#if __cpp_concepts >= 201907L + bar (0); +#endif + S s; + fn4<0, 1, 2>(); +} diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C new file mode 100644 index 00000000000..c3c67d6ea34 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C @@ -0,0 +1,111 @@ +// P2662R3 - Pack Indexing +// PR c++/113798 +// { dg-do compile { target c++26 } } +// Test invalid cases. + +template +using Type = Typo...[I]; // { dg-error "does not name a type" } + +template +constexpr auto Var = Typo...[I]; // { dg-error "no parameter packs" } + +template +void foo(Ts...[]); // { dg-error "pack index missing" } + +template +void f(Ts...[1]); + +template +int f2 (X... [N]); // { dg-error "contains no parameter packs" } + +struct X; + +template +struct TX; + +template typename Tp> +void +bad (int i) +{ + i...[0]; // { dg-error "no parameter packs" } + V...[0]; // { dg-error "no parameter packs" } + X...[0] x; // { dg-error "no parameter packs" } + T...[0] t; // { dg-error "no parameter packs" } + Tp...[0] tp; // { dg-error "expected" } + + X...[0] xarr[1]; // { dg-error "no parameter packs" } + T...[0] tarr[1]; // { dg-error "no parameter packs" } + Tp...[0] tparr[1]; // { dg-error "expected" } +} + +template +int +getT (auto... Ts) +{ + return Ts...[N]; // { dg-error "pack index is out of range" } +} + +template +int +getT2 (auto... Ts) +{ + return Ts...[N]; // { dg-error "pack index is negative" } +} + +template +void +badtype () +{ + Ts...[N] t; // { dg-error "pack index is out of range" } +} + +template +void +badtype2 () +{ + Ts...[N] t; // { dg-error "pack index is negative" } +} + +int nonconst () { return 42; } + +template +void +badindex () +{ + Ts...[nonconst ()] t; // { dg-error "pack index is not an integral constant" } + // { dg-error "non-.constexpr. function" "" { target *-*-* } .-1 } +} + +template +struct broken { + Ts...1; // { dg-error "expected" } + Ts...[; // { dg-error "invalid" } + Ts...[1; // { dg-error "invalid" } + Ts...[]; // { dg-error "pack index missing" } + + void foo (auto...Vs) { + decltype(Vs...[1]) d1 = Vs...[]; // { dg-error "pack index missing" } + decltype(Vs...[1]) d2 = Vs...[; // { dg-error "expected" } + } +}; + +int main() +{ + // void f(int [1], double [1]) + f(nullptr, nullptr); // { dg-error "cannot convert" } + bad(42); + + getT<0>(); // { dg-message "required from here" } + getT<1>(); // { dg-message "required from here" } + getT2<-1>(); // { dg-message "required from here" } + + badtype<0>(); // { dg-message "required from here" } + badtype<1, int>(); // { dg-message "required from here" } + badtype2<-1>(); // { dg-message "required from here" } + badtype2<-1, int>(); // { dg-message "required from here" } + + badindex(); + + bool b = nothere...[0]; // { dg-error "no parameter packs" } + using E = nothere...[0]; // { dg-error "does not name a type" } +} diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C new file mode 100644 index 00000000000..8c10b307f3a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C @@ -0,0 +1,41 @@ +// P2662R3 - Pack Indexing +// PR c++/113798 +// { dg-do compile { target c++26 } } +// From LLVM's cxx2c-pack-indexing.cpp. + +template +struct X { }; + +template +requires requires(T...[0]) { {T...[0](0)}; } +struct S : T...[1] { + [[maybe_unused]] T...[1] base = {}; + using foo = T...[1]; + S() : T...[1]() { } + X x; + const T...[0] f(T...[0]&& parm) noexcept((T...[0])0) { + T...[0] (*test)(const volatile T...[0]**); + thread_local T...[0] d; + [[maybe_unused]] T...[0] a = parm; + auto ptr = new T...[0](0); + (*ptr).~T...[0](); + return T...[0](0); + typename T...[1]::foo b = 0; + T...[1]::i = 0; + return (T...[0])(a); + new T...[0]; + [[maybe_unused]] auto l = [](T...[0][1]) -> T...[0]{ return {}; }; + [[maybe_unused]] auto _ = l.template operator()({0}); + } + operator T...[0]() const { } +}; + +struct base { + using foo = int; + static inline int i = 42; +}; + +int main() +{ + S().f(0); +} diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C new file mode 100644 index 00000000000..8decf3064bc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C @@ -0,0 +1,65 @@ +// P2662R3 - Pack Indexing +// PR c++/113798 +// { dg-do compile { target c++26 } } +// From LLVM's cxx2c-pack-indexing.cpp. + +template +constexpr bool is_same = false; +template +constexpr bool is_same = true; + +template +constexpr bool +f (auto&&... p) +{ + return is_same; +} + +void +g () +{ + int a = 0; + const int b = 0; + static_assert(f(0)); + static_assert(f(a)); + static_assert(f(b)); +} + +template +struct A { + enum E { + x = p...[0] + }; +}; +static_assert(A<42>::x == 42); + +struct S { }; +template +constexpr auto constant_initializer = p...[0]; +constexpr auto InitOk = constant_initializer; + +consteval int evaluate(auto... p) { + return p...[0]; +} +constexpr int x = evaluate(42, S{}); +static_assert(x == 42); + +template +struct IL{}; + +template +struct TL{}; + +template +struct SpliceImpl; + +template +struct SpliceImpl, IL> +{ + using type = TL; +}; + +template +using Splice = typename SpliceImpl::type; +using type = Splice, IL<1, 2>>; +static_assert(is_same>); diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C new file mode 100644 index 00000000000..901956e2dae --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C @@ -0,0 +1,41 @@ +// P2662R3 - Pack Indexing +// PR c++/113798 +// { dg-do compile { target c++26 } } + +template struct same_type; +template struct same_type {}; + +void +fn1 (auto... Ts) +{ + same_type(); + same_type(); + same_type(); + same_type(); +} + +template +void +fn2 () +{ + same_type(); + same_type(); + same_type(); + same_type(); + same_type(); + same_type(); + same_type(); + same_type(); + same_type(); + same_type(); +} + +static constexpr unsigned char c = 'A'; + +void +g () +{ + int i = 42; + fn1 (i, 42u); + fn2<0, 1u, 2.0, 3.f, c>(); +}