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.129.124]) by sourceware.org (Postfix) with ESMTPS id 16A6A3858C74 for ; Thu, 29 Feb 2024 19:17:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 16A6A3858C74 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 16A6A3858C74 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=1709234252; cv=none; b=ltnQJFGhtuDKTvGmfvdvQU0/FCUh/uBoQ6ui4aP05mxdQUOMg3I+WD6Dr+hGbU3F0SYHWznZzsHcaMQ7Si8+eawnsaliGOTdPsV5peMBqCs0hW/d5AkAeiRIbWcv+dFl0mxtDMapyygUjEjfbFBgRTBM1xBpNyFyqPGIUVR1too= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1709234252; c=relaxed/simple; bh=wEfhfZK7IpZ1zTxFmmPDUW4I6WI8+/7l6GBg0E/x3L8=; h=DKIM-Signature:From:Date:To:Subject:Message-ID:MIME-Version; b=rTF3PHeY/BWeLmBttEQlPo3jsu77WRrOFRzX33rFBCkqDHMqlsQiYGwDJJp9BbCfdhnomz053QNK1nSfaf6uc29j4wt+Z5gXgYpT/cFSIOdGYGgftCuA9dMAS2ZuVrZIQf1xBqoei3B9sYGEmRo5EAllbvxzNSviHVmb+QIdzwU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1709234248; 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: in-reply-to:in-reply-to:references:references; bh=p2CqVbrsWFmwQRFtBnVRB8DCFHEfFicStUuAQ0QUaMs=; b=i1a/g4j9IImCzszAHrd4ijsIXskm2XKLoABHiYWzmO4Tlq3Mu6qJCG6ztJTgcYZLzsT7F5 pRKq7kBpn2qFpjzP4AXmrBxE45v1OPbvr548vjiOv22jstRQsxZK5TMNRmqcDtxJHt3lvk SZsDtH2xec3+G11gAseSwBh2jS3Umx0= Received: from mail-qk1-f200.google.com (mail-qk1-f200.google.com [209.85.222.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-321-DJC2sUoxPg62IwLh0LzF9Q-1; Thu, 29 Feb 2024 14:17:25 -0500 X-MC-Unique: DJC2sUoxPg62IwLh0LzF9Q-1 Received: by mail-qk1-f200.google.com with SMTP id af79cd13be357-787b8ae0c15so129332685a.1 for ; Thu, 29 Feb 2024 11:17:25 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1709234244; x=1709839044; h=mime-version:references:message-id:in-reply-to:subject:cc:to:date :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=p2CqVbrsWFmwQRFtBnVRB8DCFHEfFicStUuAQ0QUaMs=; b=sQC+5O/wkYSxDYivbW7T4EqdmXnKz4jtemM/Zg/DaLXwtUG86JcF6KVBCHf5+VKT/2 mWT82czcvyZthmPG/2102duvW33E7vsI1THcr40czvP2NX0/CDEqnAtN5vav0frR3qY8 u+f63KEBbVa/VIrmNeTy+kcyJD1Yk+x9MyoBiGhxEBoAH3032me56fpFiIy+U0uGaDoM pJt220IdcuylP6eo/8rEn3JfEkrsPBihDZfnDoBnbyuOoAu02y6cAdPrAiB0mqU+gOs9 F6yAcmEd3wHDD8gZdId9xzcZZnB37o2L6/W2TWa0Ox+vBHYz/ASQo8FNQNM3GKR4DIhB Xixg== X-Forwarded-Encrypted: i=1; AJvYcCXlpvhchGQYGEfAZfvD1aXGRep6dqpMF9TGN/qJ3b0LrE7AHMP0askrxmnIqvLqTQUgJhaQ51XOxaBY6l+6obYEP0LUdV3h6w== X-Gm-Message-State: AOJu0Yyrg7yiN7ruAhx1V62DYk4q9pQY++a1f0VeJAxLnDT1mWLvZvm0 M1d9jdMsd0FS2rZ2i3btRL1r5Gv4LRxtKLI9OCC3lXHt03jcKejgM4l9/sahvWgERra72Ivw7Ab gXta3xGQU1PZHwuxol9pvkpelxPYFG70eNbX9POrnAuC6AU6kJCZZG1OhJf3nD+s= X-Received: by 2002:a05:620a:10b1:b0:787:f469:9d1c with SMTP id h17-20020a05620a10b100b00787f4699d1cmr3115199qkk.18.1709234244129; Thu, 29 Feb 2024 11:17:24 -0800 (PST) X-Google-Smtp-Source: AGHT+IGZYW94KWPAiCHBDBxt0aqlyj0g52Y0L+E8FcLBOsklea0otvXZfc7AM7JLpSDh1fKE/2dJbg== X-Received: by 2002:a05:620a:10b1:b0:787:f469:9d1c with SMTP id h17-20020a05620a10b100b00787f4699d1cmr3115181qkk.18.1709234243676; Thu, 29 Feb 2024 11:17:23 -0800 (PST) Received: from [192.168.1.130] (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id vv11-20020a05620a562b00b00787c142ec1fsm943084qkn.110.2024.02.29.11.17.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Feb 2024 11:17:23 -0800 (PST) From: Patrick Palka X-Google-Original-From: Patrick Palka Date: Thu, 29 Feb 2024 14:17:22 -0500 (EST) To: Jason Merrill cc: Patrick Palka , gcc-patches@gcc.gnu.org Subject: Re: [PATCH] c++: auto(x) partial substitution [PR110025, PR114138] In-Reply-To: Message-ID: References: <20240227204839.3396154-1-ppalka@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII X-Spam-Status: No, score=-13.8 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,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 Wed, 28 Feb 2024, Jason Merrill wrote: > On 2/27/24 15:48, Patrick Palka wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look > > OK for trunk and perhaps 13? > > > > -- >8 -- > > > > In r12-6773-g09845ad7569bac we gave CTAD placeholders a level of 0 and > > ensured we never replaced them via tsubst. It turns out that autos > > representing an explicit cast need the same treatment and for the same > > reason: such autos appear in an expression context and so their level > > gets easily messed up after partial substitution, leading to premature > > replacement via an incidental tsubst instead of via do_auto_deduction. > > > > This patch fixes this by extending the r12-6773 approach to auto(x) and > > auto{x}. > > > > PR c++/110025 > > PR c++/114138 > > > > gcc/cp/ChangeLog: > > > > * cp-tree.h (make_cast_auto): Declare. > > * parser.cc (cp_parser_functional_cast): Replace a parsed auto > > with a level-less one via make_cast_auto. > > * pt.cc (find_parameter_packs_r): Don't treat level-less auto > > as a type parameter pack. > > (tsubst) : Generalized CTAD placeholder > > handling to all level-less autos. > > (make_cast_auto): Define. > > (do_auto_deduction): Handle deduction of a level-less non-CTAD > > auto. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp23/auto-fncast16.C: New test. > > * g++.dg/cpp23/auto-fncast17.C: New test. > > * g++.dg/cpp23/auto-fncast18.C: New test. > > --- > > gcc/cp/cp-tree.h | 1 + > > gcc/cp/parser.cc | 11 ++++ > > gcc/cp/pt.cc | 31 +++++++++- > > gcc/testsuite/g++.dg/cpp23/auto-fncast16.C | 12 ++++ > > gcc/testsuite/g++.dg/cpp23/auto-fncast17.C | 15 +++++ > > gcc/testsuite/g++.dg/cpp23/auto-fncast18.C | 71 ++++++++++++++++++++++ > > 6 files changed, 138 insertions(+), 3 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast16.C > > create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast17.C > > create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast18.C > > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > > index 04c3aa6cd91..6f1da1c7bad 100644 > > --- a/gcc/cp/cp-tree.h > > +++ b/gcc/cp/cp-tree.h > > @@ -7476,6 +7476,7 @@ extern tree make_decltype_auto > > (void); > > extern tree make_constrained_auto (tree, tree); > > extern tree make_constrained_decltype_auto (tree, tree); > > extern tree make_template_placeholder (tree); > > +extern tree make_cast_auto (void); > > extern bool template_placeholder_p (tree); > > extern bool ctad_template_p (tree); > > extern bool unparenthesized_id_or_class_member_access_p (tree); > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > > index 3ee9d49fb8e..1e518e6ef51 100644 > > --- a/gcc/cp/parser.cc > > +++ b/gcc/cp/parser.cc > > @@ -33314,6 +33314,17 @@ cp_parser_functional_cast (cp_parser* parser, tree > > type) > > if (!type) > > type = error_mark_node; > > + if (TREE_CODE (type) == TYPE_DECL > > + && is_auto (TREE_TYPE (type))) > > + type = TREE_TYPE (type); > > + > > + if (is_auto (type) > > + && !AUTO_IS_DECLTYPE (type) > > + && !PLACEHOLDER_TYPE_CONSTRAINTS (type) > > + && !CLASS_PLACEHOLDER_TEMPLATE (type)) > > + /* auto(x) and auto{x} are represented using a level-less auto. */ > > + type = make_cast_auto (); > > + > > if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) > > { > > cp_lexer_set_source_position (parser->lexer); > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > > index 2803824d11e..620fe5cdbfa 100644 > > --- a/gcc/cp/pt.cc > > +++ b/gcc/cp/pt.cc > > @@ -3921,7 +3921,8 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, > > void* data) > > parameter pack (14.6.3), or the type-specifier-seq of a type-id that > > is a pack expansion, the invented template parameter is a template > > parameter pack. */ > > - if (ppd->type_pack_expansion_p && is_auto (t)) > > + if (ppd->type_pack_expansion_p && is_auto (t) > > + && TEMPLATE_TYPE_LEVEL (t) != 0) > > TEMPLATE_TYPE_PARAMETER_PACK (t) = true; > > if (TEMPLATE_TYPE_PARAMETER_PACK (t)) > > parameter_pack_p = true; > > @@ -16297,9 +16298,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, > > tree in_decl) > > } > > case TEMPLATE_TYPE_PARM: > > - if (template_placeholder_p (t)) > > + if (TEMPLATE_TYPE_LEVEL (t) == 0) > > { > > + /* Level-less auto must be replaced via do_auto_deduction. */ > > This comment could use clarification about the CTAD case. Fixed. > > > + gcc_checking_assert (is_auto (t)); > > tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t); > > + if (!tmpl) > > + return t; > > + > > tmpl = tsubst_expr (tmpl, args, complain, in_decl); > > if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM) > > tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl); > > @@ -29311,6 +29317,17 @@ template_placeholder_p (tree t) > > return is_auto (t) && CLASS_PLACEHOLDER_TEMPLATE (t); > > } > > +/* Return an auto for an explicit cast, e.g. auto(x) or auto{x}. > > + Like CTAD placeholders, these have level 0 so that they're not > > + accidentally replaced via tsubst, and are always directly resolved > > + via do_auto_deduction. */ > > + > > +tree > > +make_cast_auto () > > +{ > > + return make_auto_1 (auto_identifier, true, /*level=*/0); > > +} > > + > > /* Make a "constrained auto" type-specifier. This is an auto or > > decltype(auto) type with constraints that must be associated after > > deduction. The constraint is formed from the given concept CON > > @@ -31213,7 +31230,15 @@ do_auto_deduction (tree type, tree init, tree > > auto_node, > > } > > } > > - if (TEMPLATE_TYPE_LEVEL (auto_node) == 1) > > + if (TEMPLATE_TYPE_LEVEL (auto_node) == 0) > > + { > > + /* Level-less auto can't be replaced via tsubst, so do it directly. > > */ > > I wonder about, rather than returning it directly, setting its level to 1 for > the substitution? Done, that works nicely. > > Then I wonder if it would be feasible to give all autos level 0 and adjust it > here? That's probably not a stage 4 change, though... It seems feasible. I experimented doing this in the past[1] and ran into two complications. One complication was with constrained auto deduction, e.g. template void g() { C auto x = ...; }; Here the underlying concept-id that we enter satisfaction with is C where this auto has level one greater than the template depth, and the argument vector we pass has an extra innermost level containing the deduced type, so things match up nicely. This seems to be the only place where we truly need auto to have a non 0/1 level. In my WIP patch in that thread I just made do_auto_deduction build the concept-id C in terms of an auto of the proper level before entering satisfaction, which was kind of ugly but worked. The other complication was with Concepts TS extended auto deduction: tuple t = tuple{}; because unify_pack_expansion (called from fn_type_unification during do_auto_deduction) isn't prepared to see a parameter pack of level 0 (unify has no problems with ordinary tparms of level 0 though). This shouldn't be too hard to fix though. How does the following look for trunk and perhaps 13 (there should be no functional change for code that doesn't use auto(x))? [1]: https://gcc.gnu.org/pipermail/gcc-patches/2022-January/587818.html -- >8 -- PR c++/110025 PR c++/114138 gcc/cp/ChangeLog: * cp-tree.h (make_cast_auto): Declare. * parser.cc (cp_parser_functional_cast): Replace a parsed auto with a level-less one via make_cast_auto. * pt.cc (find_parameter_packs_r): Don't treat level-less auto as a type parameter pack. (tsubst) : Generalized CTAD placeholder handling to all level-less autos. (make_cast_auto): Define. (do_auto_deduction): Handle replacement of a level-less auto. gcc/testsuite/ChangeLog: * g++.dg/cpp23/auto-fncast16.C: New test. * g++.dg/cpp23/auto-fncast17.C: New test. * g++.dg/cpp23/auto-fncast18.C: New test. --- gcc/cp/cp-tree.h | 1 + gcc/cp/parser.cc | 11 ++++ gcc/cp/pt.cc | 37 +++++++++++- gcc/testsuite/g++.dg/cpp23/auto-fncast16.C | 12 ++++ gcc/testsuite/g++.dg/cpp23/auto-fncast17.C | 15 +++++ gcc/testsuite/g++.dg/cpp23/auto-fncast18.C | 69 ++++++++++++++++++++++ 6 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast16.C create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast17.C create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast18.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 04c3aa6cd91..6f1da1c7bad 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7476,6 +7476,7 @@ extern tree make_decltype_auto (void); extern tree make_constrained_auto (tree, tree); extern tree make_constrained_decltype_auto (tree, tree); extern tree make_template_placeholder (tree); +extern tree make_cast_auto (void); extern bool template_placeholder_p (tree); extern bool ctad_template_p (tree); extern bool unparenthesized_id_or_class_member_access_p (tree); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 3ee9d49fb8e..3dbe6722ba1 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -33314,6 +33314,17 @@ cp_parser_functional_cast (cp_parser* parser, tree type) if (!type) type = error_mark_node; + if (TREE_CODE (type) == TYPE_DECL + && is_auto (TREE_TYPE (type))) + type = TREE_TYPE (type); + + if (is_auto (type) + && !AUTO_IS_DECLTYPE (type) + && !PLACEHOLDER_TYPE_CONSTRAINTS (type) + && !CLASS_PLACEHOLDER_TEMPLATE (type)) + /* auto(x) and auto{x} are represented by level-less auto. */ + type = make_cast_auto (); + if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) { cp_lexer_set_source_position (parser->lexer); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 2803824d11e..369e33f23c7 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -3921,7 +3921,8 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) parameter pack (14.6.3), or the type-specifier-seq of a type-id that is a pack expansion, the invented template parameter is a template parameter pack. */ - if (ppd->type_pack_expansion_p && is_auto (t)) + if (ppd->type_pack_expansion_p && is_auto (t) + && TEMPLATE_TYPE_LEVEL (t) != 0) TEMPLATE_TYPE_PARAMETER_PACK (t) = true; if (TEMPLATE_TYPE_PARAMETER_PACK (t)) parameter_pack_p = true; @@ -16297,9 +16298,19 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) } case TEMPLATE_TYPE_PARM: - if (template_placeholder_p (t)) + if (TEMPLATE_TYPE_LEVEL (t) == 0) { + /* This is either an ordinary level-less auto or a CTAD placeholder + auto. These get replaced only via do_auto_deduction which, in the + ordinary case, temporarily overrides its level to 1 before calling + tsubst. CTAD placeholders are replaced via do_class_deduction. */ + gcc_checking_assert (is_auto (t)); tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t); + if (!tmpl) + /* Ordinary level-less auto, nothing to substitute. */ + return t; + + /* Substitute the template of this CTAD placeholder. */ tmpl = tsubst_expr (tmpl, args, complain, in_decl); if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM) tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl); @@ -29311,6 +29322,17 @@ template_placeholder_p (tree t) return is_auto (t) && CLASS_PLACEHOLDER_TEMPLATE (t); } +/* Return an auto for an explicit cast, e.g. auto(x) or auto{x}. + Like CTAD placeholders, these have level 0 so that they're not + accidentally replaced via tsubst, and are always directly resolved + via do_auto_deduction. */ + +tree +make_cast_auto () +{ + return make_auto_1 (auto_identifier, true, /*level=*/0); +} + /* Make a "constrained auto" type-specifier. This is an auto or decltype(auto) type with constraints that must be associated after deduction. The constraint is formed from the given concept CON @@ -31213,7 +31235,16 @@ do_auto_deduction (tree type, tree init, tree auto_node, } } - if (TEMPLATE_TYPE_LEVEL (auto_node) == 1) + if (TEMPLATE_TYPE_LEVEL (auto_node) == 0) + { + /* Substitute this level-less auto via tsubst by temporarily + overriding its level to 1. */ + TEMPLATE_TYPE_LEVEL (auto_node) = 1; + type = tsubst (type, targs, complain, NULL_TREE); + TEMPLATE_TYPE_LEVEL (auto_node) = 0; + return type; + } + else if (TEMPLATE_TYPE_LEVEL (auto_node) == 1) /* The outer template arguments are already substituted into type (but we still may have used them for constraint checking above). */; else if (context == adc_unify) diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C new file mode 100644 index 00000000000..e2c13f6b050 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C @@ -0,0 +1,12 @@ +// PR c++/110025 +// { dg-do compile { target c++23 } } + +template +struct A { }; + +template +A f(); + +int main() { + f<0>(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C new file mode 100644 index 00000000000..25186dfdbf2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C @@ -0,0 +1,15 @@ +// PR c++/110025 +// { dg-do compile { target c++23 } } + +template struct tuple; + +template +using constant_t = int; + +template +using constants_t = tuple...>; + +using ty0 = constants_t<>; +using ty1 = constants_t<1>; +using ty2 = constants_t<1, 2>; +using ty3 = constants_t<1, 2, 3>; diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C new file mode 100644 index 00000000000..4656723684f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C @@ -0,0 +1,69 @@ +// PR c++/114138 +// { dg-do compile { target c++23 } } + +namespace std { + template + T&& declval() noexcept requires true; + + template + void declval() noexcept; + + namespace detail { + struct none_such; + template + using none_such_t = none_such; + + template + extern const none_such_t _getter_for; + + template + using _decay_t = decltype(auto(declval())); + + static_assert(__is_same_as(_decay_t, void)); + } + + template + using _result_of_t = decltype(Fn(declval()...)); + + template + using tuple_element_t = _result_of_t>, char(*)[I+1], Tuple>; + + template + struct pair { + First first; + Second second; + }; + + template + inline constexpr bool _is_pair = false; + + template + inline constexpr bool _is_pair> = true; + + template + concept Pair = _is_pair()))>; + + template + requires (I <= 1) + decltype(auto) get(P&& p) noexcept { + if constexpr (I == 0) { + return (static_cast(p).first); + } else { + return (static_cast(p).second); + } + } + + namespace detail { + inline constexpr auto _pair_getter = + [](char(*)[J], Pair&& p) noexcept -> decltype(auto) { + return std::get(static_cast(p)); + }; + + template + inline constexpr auto _getter_for> = _pair_getter; + } + +} + +static_assert(__is_same_as(int&, std::tuple_element_t<0, std::pair&>)); +static_assert(__is_same_as(float&&, std::tuple_element_t<1, std::pair&&>)); -- 2.44.0.53.g0f9d4d28b7