From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id D1B493858D1E for ; Tue, 18 Apr 2023 19:09:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D1B493858D1E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681844979; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+ILVRqgqbF1y52MCFJh1ytbyeldjQXYVCf/+a7DLRGY=; b=bBNCqPgxZEBUmLjaaVIjJsDQxFYqC43JY2aZE6/MDVZLWFP7FHN9ABKW0xLsPrvjwtXWQm aLpxb9+SqlVoK/mF60DCBlUCIm6QAABSdxRBcMt18Nx+Snti4W+x6quS6fcf0VOaGz6z40 ToT2vQdbb2OM0g2gNqUNoljQvpxkwD0= Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-672-WTomHwuoNwep39QX0Khxag-1; Tue, 18 Apr 2023 15:09:37 -0400 X-MC-Unique: WTomHwuoNwep39QX0Khxag-1 Received: by mail-qt1-f199.google.com with SMTP id 19-20020ac84e93000000b003e3c23d562aso21205619qtp.1 for ; Tue, 18 Apr 2023 12:09:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681844977; x=1684436977; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+ILVRqgqbF1y52MCFJh1ytbyeldjQXYVCf/+a7DLRGY=; b=cATd3AlEzkLguQ7Eip71UL9JLBTj8fLRQ7R8cETHyQmQCK7T8f+RIsg4N4CHiAWqrL ZBNR3mDoaF2hc0pbRazwGJyCElDGai0XXtx2pw5JM55YCNui+SmPpksJzTWMZUtgjZZO IOu0/jMKjeFUNvtboTIa0KuwtzcyRIl6SrljhY6I+6WPaTAezwr3BEqIQxK6M3nKvPso z6VIbL5/tZyRnV7hnzUN3aff8IGFOcbsxoo3g8wntxDUeBdVEK3u6LecLvgWRFiiADSk Xpi3ZLbJdpEWvIugHevuskAgUgGcrc7vN4uLsVPQc0EGcl0tm6bplrhHVcpfwUNPZS0K ZRaw== X-Gm-Message-State: AAQBX9cjmuameLkg76nk0WPbL0kN5Mt4M328ekQBuxsqb487TNqThZ6V 8Dnp+Oa4o5oWmWmdIuKucvOMYf73lFPSoS82pV/ZDLKasOMxyXbQLTtK2cG+KowQVd7Na/06H8i Tq2SuB0VXgaavslhE+Q== X-Received: by 2002:a05:622a:28f:b0:3e3:7e6f:423c with SMTP id z15-20020a05622a028f00b003e37e6f423cmr1702408qtw.34.1681844976829; Tue, 18 Apr 2023 12:09:36 -0700 (PDT) X-Google-Smtp-Source: AKy350bEyBnruAAgVPRCPPZ8SBt1N03qajQSxHKCS9ZWWYgxHMlLlRyWQcb31M1tGgmXJ7W6vJN81A== X-Received: by 2002:a05:622a:28f:b0:3e3:7e6f:423c with SMTP id z15-20020a05622a028f00b003e37e6f423cmr1702340qtw.34.1681844976299; Tue, 18 Apr 2023 12:09:36 -0700 (PDT) Received: from [192.168.1.108] (130-44-146-16.s12558.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.146.16]) by smtp.gmail.com with ESMTPSA id dz6-20020a05620a2b8600b0074df51a90b6sm1643022qkb.60.2023.04.18.12.09.35 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 18 Apr 2023 12:09:35 -0700 (PDT) Message-ID: Date: Tue, 18 Apr 2023 15:09:34 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.10.0 Subject: Re: [PATCH] c++: Define built-in for std::tuple_element [PR100157] To: Patrick Palka Cc: Jonathan Wakely , libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org, Marek Polacek References: <20220707171436.1419387-1-jwakely@redhat.com> <2c2d32db-170d-d6b7-d04f-6cf27e157321@idea> <5ea335c0-c12a-59d6-4dfb-d62b402654d2@idea> <235cd04b-dd0d-9a3d-c371-f72baf407d7e@redhat.com> <5a926456-4b93-822e-7747-c699b611d359@redhat.com> <1ae002da-0516-374f-6532-221022bdc53a@idea> From: Jason Merrill In-Reply-To: <1ae002da-0516-374f-6532-221022bdc53a@idea> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-13.9 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,NICE_REPLY_A,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 4/11/23 10:21, Patrick Palka wrote: > On Thu, 26 Jan 2023, Jason Merrill wrote: > >> On 1/25/23 15:35, Patrick Palka wrote: >>> On Tue, 17 Jan 2023, Jason Merrill wrote: >>> >>>> On 1/9/23 14:25, Patrick Palka via Gcc-patches wrote: >>>>> On Mon, 9 Jan 2023, Patrick Palka wrote: >>>>> >>>>>> On Wed, 5 Oct 2022, Patrick Palka wrote: >>>>>> >>>>>>> On Thu, 7 Jul 2022, Jonathan Wakely via Gcc-patches wrote: >>>>>>> >>>>>>>> This adds a new built-in to replace the recursive class template >>>>>>>> instantiations done by traits such as std::tuple_element and >>>>>>>> std::variant_alternative. The purpose is to select the Nth type >>>>>>>> from a >>>>>>>> list of types, e.g. __builtin_type_pack_element(1, char, int, >>>>>>>> float) >>>>>>>> is >>>>>>>> int. >>>>>>>> >>>>>>>> For a pathological example tuple_element_t<1000, tuple<2000 >>>>>>>> types...>> >>>>>>>> the compilation time is reduced by more than 90% and the memory >>>>>>>> used >>>>>>>> by >>>>>>>> the compiler is reduced by 97%. In realistic examples the gains >>>>>>>> will >>>>>>>> be >>>>>>>> much smaller, but still relevant. >>>>>>>> >>>>>>>> Clang has a similar built-in, __type_pack_element, but >>>>>>>> that's >>>>>>>> a >>>>>>>> "magic template" built-in using <> syntax, which GCC doesn't >>>>>>>> support. >>>>>>>> So >>>>>>>> this provides an equivalent feature, but as a built-in function >>>>>>>> using >>>>>>>> parens instead of <>. I don't really like the name "type pack >>>>>>>> element" >>>>>>>> (it gives you an element from a pack of types) but the >>>>>>>> semi-consistency >>>>>>>> with Clang seems like a reasonable argument in favour of keeping >>>>>>>> the >>>>>>>> name. I'd be open to alternative names though, e.g. >>>>>>>> __builtin_nth_type >>>>>>>> or __builtin_type_at_index. >>>>>>> >>>>>>> Rather than giving the trait a different name from >>>>>>> __type_pack_element, >>>>>>> I wonder if we could just special case cp_parser_trait to expect <> >>>>>>> instead of parens for this trait? >>>>>>> >>>>>>> Btw the frontend recently got a generic TRAIT_TYPE tree code, which >>>>>>> gets >>>>>>> rid of much of the boilerplate of adding a new type-yielding >>>>>>> built-in >>>>>>> trait, see e.g. cp-trait.def. >>>>>> >>>>>> Here's a tested patch based on Jonathan's original patch that >>>>>> implements >>>>>> the built-in in terms of TRAIT_TYPE, names it __type_pack_element >>>>>> instead of __builtin_type_pack_element, and treats invocations of it >>>>>> like a template-id instead of a call (to match Clang). >>>>>> >>>>>> -- >8 -- >>>>>> >>>>>> Subject: [PATCH] c++: Define built-in for std::tuple_element >>>>>> [PR100157] >>>>>> >>>>>> This adds a new built-in to replace the recursive class template >>>>>> instantiations done by traits such as std::tuple_element and >>>>>> std::variant_alternative. The purpose is to select the Nth type from >>>>>> a >>>>>> list of types, e.g. __type_pack_element<1, char, int, float> is int. >>>>>> We implement it as a special kind of TRAIT_TYPE. >>>>>> >>>>>> For a pathological example tuple_element_t<1000, tuple<2000 types...>> >>>>>> the compilation time is reduced by more than 90% and the memory used >>>>>> by >>>>>> the compiler is reduced by 97%. In realistic examples the gains will >>>>>> be >>>>>> much smaller, but still relevant. >>>>>> >>>>>> Unlike the other built-in traits, __type_pack_element uses template-id >>>>>> syntax instead of call syntax and is SFINAE-enabled, matching Clang's >>>>>> implementation. And like the other built-in traits, it's not >>>>>> mangleable >>>>>> so we can't use it directly in function signatures. >>>>>> >>>>>> Some caveats: >>>>>> >>>>>> * Clang's version of the built-in seems to act like a "magic >>>>>> template" >>>>>> that can e.g. be used as a template template argument. For >>>>>> simplicity >>>>>> we implement it in a more ad-hoc way. >>>>>> * Our parsing of the <>'s in __type_pack_element<...> is currently >>>>>> rudimentary and doesn't try to disambiguate a trailing >> vs > > >>>>>> as cp_parser_enclosed_template_argument_list does. >>>>> >>>>> Hmm, this latter caveat turns out to be inconvenient (for code such as >>>>> type_pack_element3.C) and admits an easy workaround inspired by what >>>>> cp_parser_enclosed_template_argument_list does. >>>>> >>>>> v2: Consider the >> in __type_pack_element<0, int, char>> to be two >'s. >>>>> Handle non-type TRAIT_TYPE_TYPE1 in strip_typedefs (for sake of >>>>> CPTK_TYPE_PACK_ELEMENT). >>>> >>>> Why not use cp_parser_enclosed_template_argument_list directly? >>> >>> If we used cp_parser_enclosed_template_argument_list we would then need >>> to convert the returned TREE_VEC into a TREE_LIST and also diagnose >>> argument kind mismatches (i.e. verify the first argument is an >>> expression and the rest are types). It seemed like more complexity >>> overall then just duplicating the >> splitting logic, but I can do that >>> if you prefer? >> >> I think I would prefer that, parser stuff can be pretty subtle. >> >> Instead of turning the TREE_VEC into a TREE_LIST, we could handle TREE_VEC as >> a trait operand? > > Sorry for the late follow up... Here's an updated patch that uses > cp_parser_enclosed_template_argument_list instead of copying the parsing > logic from there. I put off handling TREE_VEC as a trait operand for > now. We could convert all variadic traits to use TREE_VEC instead of > TREE_LIST at once in a followup patch. > > Bootstrapped and regtested on x86_64-pc-linux-gnu, also tested against > libc++'s tuple/variant impl for good measure (which uses > __type_pack_element when available). > > -- >8 -- > > Subject: [PATCH] c++: Define built-in for std::tuple_element [PR100157] > > This adds a new built-in to replace the recursive class template > instantiations done by traits such as std::tuple_element and > std::variant_alternative. The purpose is to select the Nth type from a > list of types, e.g. __type_pack_element<1, char, int, float> is int. > We implement it as a special kind of TRAIT_TYPE. > > For a pathological example tuple_element_t<1000, tuple<2000 types...>> > the compilation time is reduced by more than 90% and the memory used by > the compiler is reduced by 97%. In realistic examples the gains will be > much smaller, but still relevant. > > Unlike the other built-in traits, __type_pack_element uses template-id > syntax instead of call syntax and is SFINAE-enabled, matching Clang's > implementation. And like the other built-in traits, it's not mangleable > so we can't use it directly in function signatures. > > N.B. Clang seems to implement __type_pack_element as a first-class > template that can e.g. be used as a template-template argument. For > simplicity we implement it in a more ad-hoc way. > > Co-authored-by: Jonathan Wakely > > PR c++/100157 > > gcc/cp/ChangeLog: > > * cp-trait.def (TYPE_PACK_ELEMENT): Define. > * cp-tree.h (finish_trait_type): Add complain parameter. > * cxx-pretty-print.cc (pp_cxx_trait): Handle > CPTK_TYPE_PACK_ELEMENT. > * parser.cc (cp_parser_constant_expression): Document default > arguments. > (cp_parser_trait): Handle CPTK_TYPE_PACK_ELEMENT. Pass > tf_warning_or_error to finish_trait_type. > * pt.cc (tsubst) : Handle non-type first > argument. Pass complain to finish_trait_type. > * semantics.cc (finish_type_pack_element): Define. > (finish_trait_type): Add complain parameter. Handle > CPTK_TYPE_PACK_ELEMENT. > * tree.cc (strip_typedefs): Handle non-type first argument. > Pass tf_warning_or_error to finish_trait_type. > * typeck.cc (structural_comptypes) : Use > cp_tree_equal instead of same_type_p for the first argument. > > libstdc++-v3/ChangeLog: > > * include/bits/utility.h (_Nth_type): Conditionally define in > terms of __type_pack_element if available. > * testsuite/20_util/tuple/element_access/get_neg.cc: Prune > additional errors from the new built-in. > > gcc/testsuite/ChangeLog: > > * g++.dg/ext/type_pack_element1.C: New test. > * g++.dg/ext/type_pack_element2.C: New test. > * g++.dg/ext/type_pack_element3.C: New test. > --- > gcc/cp/cp-trait.def | 1 + > gcc/cp/cp-tree.h | 2 +- > gcc/cp/cxx-pretty-print.cc | 21 ++++++--- > gcc/cp/parser.cc | 45 ++++++++++++++++--- > gcc/cp/pt.cc | 8 +++- > gcc/cp/semantics.cc | 39 +++++++++++++++- > gcc/cp/tree.cc | 10 +++-- > gcc/cp/typeck.cc | 2 +- > gcc/testsuite/g++.dg/ext/type_pack_element1.C | 19 ++++++++ > gcc/testsuite/g++.dg/ext/type_pack_element2.C | 14 ++++++ > gcc/testsuite/g++.dg/ext/type_pack_element3.C | 22 +++++++++ > libstdc++-v3/include/bits/utility.h | 6 +++ > .../20_util/tuple/element_access/get_neg.cc | 1 + > 13 files changed, 170 insertions(+), 20 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/ext/type_pack_element1.C > create mode 100644 gcc/testsuite/g++.dg/ext/type_pack_element2.C > create mode 100644 gcc/testsuite/g++.dg/ext/type_pack_element3.C > > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def > index bac593c0094..8b7fece0cc8 100644 > --- a/gcc/cp/cp-trait.def > +++ b/gcc/cp/cp-trait.def > @@ -91,6 +91,7 @@ DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1) > DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1) > DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1) > DEFTRAIT_TYPE (UNDERLYING_TYPE, "__underlying_type", 1) > +DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1) > > /* These traits yield a type pack, not a type, and are represented by > cp_parser_trait as a special BASES tree instead of a TRAIT_TYPE tree. */ > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 622752ae4e6..1c897c2ce8f 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -7761,7 +7761,7 @@ extern tree finish_decltype_type (tree, bool, tsubst_flags_t); > extern tree fold_builtin_is_corresponding_member (location_t, int, tree *); > extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, int, tree *); > extern tree finish_trait_expr (location_t, enum cp_trait_kind, tree, tree); > -extern tree finish_trait_type (enum cp_trait_kind, tree, tree); > +extern tree finish_trait_type (enum cp_trait_kind, tree, tree, tsubst_flags_t); > extern tree build_lambda_expr (void); > extern tree build_lambda_object (tree); > extern tree begin_lambda_type (tree); > diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc > index 7f4556d0da2..c33919873f1 100644 > --- a/gcc/cp/cxx-pretty-print.cc > +++ b/gcc/cp/cxx-pretty-print.cc > @@ -2625,11 +2625,19 @@ pp_cxx_trait (cxx_pretty_printer *pp, tree t) > #undef DEFTRAIT > } > > - pp_cxx_left_paren (pp); > - if (TYPE_P (type1)) > - pp->type_id (type1); > + if (kind == CPTK_TYPE_PACK_ELEMENT) > + { > + pp_cxx_begin_template_argument_list (pp); > + pp->expression (type1); > + } > else > - pp->expression (type1); > + { > + pp_cxx_left_paren (pp); > + if (TYPE_P (type1)) > + pp->type_id (type1); > + else > + pp->expression (type1); > + } > if (type2) > { > if (TREE_CODE (type2) != TREE_LIST) > @@ -2644,7 +2652,10 @@ pp_cxx_trait (cxx_pretty_printer *pp, tree t) > pp->type_id (TREE_VALUE (arg)); > } > } > - pp_cxx_right_paren (pp); > + if (kind == CPTK_TYPE_PACK_ELEMENT) > + pp_cxx_end_template_argument_list (pp); > + else > + pp_cxx_right_paren (pp); > } > > // requires-clause: > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index a6341b98af2..281292b462d 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -10730,9 +10730,9 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, > > static cp_expr > cp_parser_constant_expression (cp_parser* parser, > - int allow_non_constant_p, > - bool *non_constant_p, > - bool strict_p) > + int allow_non_constant_p /* = 0 */, > + bool *non_constant_p /* = NULL */, > + bool strict_p /* = false */) > { > bool saved_integral_constant_expression_p; > bool saved_allow_non_integral_constant_expression_p; > @@ -10959,10 +10959,10 @@ cp_parser_trait (cp_parser* parser, enum rid keyword) > cp_lexer_consume_token (parser->lexer); > > matching_parens parens; > - parens.require_open (parser); > > if (kind == CPTK_IS_DEDUCIBLE) > { > + parens.require_open (parser); > const cp_token* token = cp_lexer_peek_token (parser->lexer); > type1 = cp_parser_id_expression (parser, > /*template_keyword_p=*/false, > @@ -10972,8 +10972,18 @@ cp_parser_trait (cp_parser* parser, enum rid keyword) > /*optional_p=*/false); > type1 = cp_parser_lookup_name_simple (parser, type1, token->location); > } > + else if (kind == CPTK_TYPE_PACK_ELEMENT) > + { > + /* __type_pack_element takes an expression as its first argument and uses > + template-id syntax instead of function call syntax (for consistency > + with Clang). We special case these properties of __type_pack_element > + here and elsewhere. */ > + cp_parser_require (parser, CPP_LESS, RT_LESS); > + type1 = cp_parser_constant_expression (parser); > + } > else > { > + parens.require_open (parser); > type_id_in_expr_sentinel s (parser); > type1 = cp_parser_type_id (parser); > } > @@ -10981,7 +10991,24 @@ cp_parser_trait (cp_parser* parser, enum rid keyword) > if (type1 == error_mark_node) > return error_mark_node; > > - if (binary) > + if (kind == CPTK_TYPE_PACK_ELEMENT) > + { > + cp_parser_require (parser, CPP_COMMA, RT_COMMA); > + tree rest = cp_parser_enclosed_template_argument_list (parser); > + for (tree elt : tree_vec_range (rest)) > + { > + if (!TYPE_P (elt)) > + { > + error_at (cp_expr_loc_or_input_loc (elt), > + "trailing argument to %<__type_pack_element%> " > + "is not a type"); > + return error_mark_node; > + } > + type2 = tree_cons (NULL_TREE, elt, type2); > + } > + type2 = nreverse (type2); > + } > + else if (binary) > { > cp_parser_require (parser, CPP_COMMA, RT_COMMA); > > @@ -11012,7 +11039,11 @@ cp_parser_trait (cp_parser* parser, enum rid keyword) > } > > location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location; > - parens.require_close (parser); > + if (kind == CPTK_TYPE_PACK_ELEMENT) > + /* cp_parser_enclosed_template_argument_list above already took care > + of parsing closing '>'. */; > + else > + parens.require_close (parser); > > /* Construct a location of the form: > __is_trivially_copyable(_Tp) > @@ -11030,7 +11061,7 @@ cp_parser_trait (cp_parser* parser, enum rid keyword) > return cp_expr (finish_bases (type1, true), trait_loc); > default: > if (type) > - return finish_trait_type (kind, type1, type2); > + return finish_trait_type (kind, type1, type2, tf_warning_or_error); > else > return finish_trait_expr (trait_loc, kind, type1, type2); > } > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index 185e23f642d..dec410dbaba 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -16695,9 +16695,13 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) > > case TRAIT_TYPE: > { > - tree type1 = tsubst (TRAIT_TYPE_TYPE1 (t), args, complain, in_decl); > + tree type1 = TRAIT_TYPE_TYPE1 (t); > + if (TYPE_P (type1)) > + type1 = tsubst (type1, args, complain, in_decl); > + else > + type1 = tsubst_copy_and_build (type1, args, complain, in_decl); > tree type2 = tsubst (TRAIT_TYPE_TYPE2 (t), args, complain, in_decl); In the case of __builtin_type_pack_element where the original is something like T..., we could optimize even further and select the element without actually doing the expansion? The patch is OK for trunk as is. Jason > - type = finish_trait_type (TRAIT_TYPE_KIND (t), type1, type2); > + type = finish_trait_type (TRAIT_TYPE_KIND (t), type1, type2, complain); > return cp_build_qualified_type (type, > cp_type_quals (t) | cp_type_quals (type), > complain | tf_ignore_bad_quals); > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > index 99a76e3ed65..74b852c500a 100644 > --- a/gcc/cp/semantics.cc > +++ b/gcc/cp/semantics.cc > @@ -4470,6 +4470,36 @@ finish_underlying_type (tree type) > return underlying_type; > } > > +/* 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) > +{ > + 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"); > + return error_mark_node; > + } > + HOST_WIDE_INT val = tree_to_shwi (idx); > + if (val < 0) > + { > + if (complain & tf_error) > + error ("%<__type_pack_element%> index is negative"); > + return error_mark_node; > + } > + tree result = chain_index (val, types); > + if (!result) > + { > + if (complain & tf_error) > + error ("%<__type_pack_element%> index is out of range"); > + return error_mark_node; > + } > + return TREE_VALUE (result); > +} > + > /* Implement the __direct_bases keyword: Return the direct base classes > of type. */ > > @@ -12240,7 +12270,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) > /* Process a trait type. */ > > tree > -finish_trait_type (cp_trait_kind kind, tree type1, tree type2) > +finish_trait_type (cp_trait_kind kind, tree type1, tree type2, > + tsubst_flags_t complain) > { > if (type1 == error_mark_node > || type2 == error_mark_node) > @@ -12264,17 +12295,23 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2) > { > case CPTK_UNDERLYING_TYPE: > return finish_underlying_type (type1); > + > case CPTK_REMOVE_CV: > return cv_unqualified (type1); > + > case CPTK_REMOVE_REFERENCE: > if (TYPE_REF_P (type1)) > type1 = TREE_TYPE (type1); > return type1; > + > case CPTK_REMOVE_CVREF: > if (TYPE_REF_P (type1)) > type1 = TREE_TYPE (type1); > return cv_unqualified (type1); > > + case CPTK_TYPE_PACK_ELEMENT: > + return finish_type_pack_element (type1, type2, complain); > + > #define DEFTRAIT_EXPR(CODE, NAME, ARITY) \ > case CPTK_##CODE: > #include "cp-trait.def" > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index 16b8fcb7d57..2c22fac17ee 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -1792,14 +1792,18 @@ strip_typedefs (tree t, bool *remove_attributes /* = NULL */, > break; > case TRAIT_TYPE: > { > - tree type1 = strip_typedefs (TRAIT_TYPE_TYPE1 (t), > - remove_attributes, flags); > + tree type1 = TRAIT_TYPE_TYPE1 (t); > + if (TYPE_P (type1)) > + type1 = strip_typedefs (type1, remove_attributes, flags); > + else > + type1 = strip_typedefs_expr (type1, remove_attributes, flags); > tree type2 = strip_typedefs (TRAIT_TYPE_TYPE2 (t), > remove_attributes, flags); > if (type1 == TRAIT_TYPE_TYPE1 (t) && type2 == TRAIT_TYPE_TYPE2 (t)) > result = NULL_TREE; > else > - result = finish_trait_type (TRAIT_TYPE_KIND (t), type1, type2); > + result = finish_trait_type (TRAIT_TYPE_KIND (t), type1, type2, > + tf_warning_or_error); > } > break; > case TYPE_PACK_EXPANSION: > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc > index 8b60cbbc167..53ac925a092 100644 > --- a/gcc/cp/typeck.cc > +++ b/gcc/cp/typeck.cc > @@ -1632,7 +1632,7 @@ structural_comptypes (tree t1, tree t2, int strict) > case TRAIT_TYPE: > if (TRAIT_TYPE_KIND (t1) != TRAIT_TYPE_KIND (t2)) > return false; > - if (!same_type_p (TRAIT_TYPE_TYPE1 (t1), TRAIT_TYPE_TYPE1 (t2)) > + if (!cp_tree_equal (TRAIT_TYPE_TYPE1 (t1), TRAIT_TYPE_TYPE1 (t2)) > || !cp_tree_equal (TRAIT_TYPE_TYPE2 (t1), TRAIT_TYPE_TYPE2 (t2))) > return false; > break; > diff --git a/gcc/testsuite/g++.dg/ext/type_pack_element1.C b/gcc/testsuite/g++.dg/ext/type_pack_element1.C > new file mode 100644 > index 00000000000..46858555502 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/type_pack_element1.C > @@ -0,0 +1,19 @@ > +// { dg-do compile { target c++11 } } > + > +using ty0 = __type_pack_element<0, int>; > +using ty0 = __type_pack_element<0, int, char>; > +using ty0 = int; > + > +using ty1 = __type_pack_element<1, int, char>; > +using ty1 = __type_pack_element<(6 - 5) * 1, int, char>; > +using ty1 = char; > + > +template > +using __const_type_pack_element_t = const __type_pack_element; > + > +using ty2 = __const_type_pack_element_t<2, int, char, long>; > +using ty2 = const long; > + > +template struct A { }; > +using ty3 = __type_pack_element<3, int, int, int, A>; > +using ty3 = A; > diff --git a/gcc/testsuite/g++.dg/ext/type_pack_element2.C b/gcc/testsuite/g++.dg/ext/type_pack_element2.C > new file mode 100644 > index 00000000000..1bf77534097 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/type_pack_element2.C > @@ -0,0 +1,14 @@ > +// { dg-do compile { target c++11 } } > + > +int p; > + > +using type = __type_pack_element<&p, int>; // { dg-error "not an integral constant" } > +using type = __type_pack_element<1, int>; // { dg-error "out of range" } > +using type = __type_pack_element<2, int, char>; // { dg-error "out of range" } > +using type = __type_pack_element<-1, int>; // { dg-error "negative" } > + > +template > +using __type_pack_element_t = __type_pack_element; > +// { dg-error "out of range" "" { target *-*-* } .-1 } > + > +using type = __type_pack_element_t<3, int, char, long>; // { dg-message "here" } > diff --git a/gcc/testsuite/g++.dg/ext/type_pack_element3.C b/gcc/testsuite/g++.dg/ext/type_pack_element3.C > new file mode 100644 > index 00000000000..269f84f464f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/type_pack_element3.C > @@ -0,0 +1,22 @@ > +// { dg-do compile { target c++11 } } > + > +template> > +constexpr int f(int) { return 1; } > + > +template > +constexpr int f(...) { return 2; }; > + > +int p; > + > +static_assert(f(0) == 1, ""); > +static_assert(f(0) == 1, ""); > +static_assert(f(0) == 2, ""); > +static_assert(f(0) == 2, ""); > + > +template struct A; > +template struct A> { }; > +template struct A; > + > +template struct B; > +template struct B> { }; > +template struct B; > diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h > index abaaae2dd33..4692aa0c9b0 100644 > --- a/libstdc++-v3/include/bits/utility.h > +++ b/libstdc++-v3/include/bits/utility.h > @@ -224,6 +224,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif // C++17 > #endif // C++14 > > +#if __has_builtin(__type_pack_element) > + template > + struct _Nth_type > + { using type = __type_pack_element<_Np, _Types...>; }; > +#else > template > struct _Nth_type > { }; > @@ -262,6 +267,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct _Nth_type<1, _Tp0, _Tp1, _Tp2, _Rest...> > { using type = _Tp1; }; > #endif > +#endif > > #if __cplusplus > 202002L > #define __cpp_lib_ranges_zip 202110L // for and > diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > index 9bce0b1caa2..dd0df9be1ea 100644 > --- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > +++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > @@ -61,3 +61,4 @@ test03() > > // { dg-error "tuple index must be in range" "" { target *-*-* } 0 } > // { dg-prune-output "no type named 'type' in .*_Nth_type" } > +// { dg-prune-output "'__type_pack_element' index is out of range" }