From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-yw1-x1135.google.com (mail-yw1-x1135.google.com [IPv6:2607:f8b0:4864:20::1135]) by sourceware.org (Postfix) with ESMTPS id 215503857BBD; Fri, 15 Jul 2022 19:59:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 215503857BBD Received: by mail-yw1-x1135.google.com with SMTP id 00721157ae682-31bf3656517so56749917b3.12; Fri, 15 Jul 2022 12:59:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=yePRbi/w6lQKDVrjLUiOhgVej5FWG87HhUypZO6Hsb8=; b=nFzpTdpowkGSsbOnsh3Oa/d5yrMajs0foXFM537PbjLxULDyg6g39P5b3MYfVP3tJE x8YinGO1RSikxVk+V7TfSnk5yEKx2ZrPzAHlACv9iCLMr9xqdNtHJMqat532VdAfER0d ggqPW8fFzWi7WxdhZoFTL54Bri8XSrxoLbH/piiKRCzmvQdTembuhySJDSw4mYpdsFn+ vh0iRPknudl25ghrnq/Z6E3RzGWUglZ5XP7+UPu1QsdkhDzbaT3DtmEWMtkQhjjSDSLL io9DUJgFCO2Qyy7GifrSJFCgfT3tgvA3gk41eCjFNIUtSmky2czYCH+KAjcZeR3ZEodE 1y2A== X-Gm-Message-State: AJIora/FNaJHoW7FU0MQF+O8LLGrTq0gcSfzd4tM2U38YM4mXkpngxCP Jn9epv1Vbxbz5YlBXE4umMXTmkkEH2TgVix2EGZJBvmS X-Google-Smtp-Source: AGRyM1vkHIkGTb3n2pOyCFWDtAQl03Ij+Vgaj11RjBiikIXKv6fNg+kj81k1ZRm2fOFUEXhB8iBZ3yL9cutprUAamKs= X-Received: by 2002:a0d:f647:0:b0:31d:17cc:3337 with SMTP id g68-20020a0df647000000b0031d17cc3337mr17729254ywf.100.1657915193642; Fri, 15 Jul 2022 12:59:53 -0700 (PDT) MIME-Version: 1.0 References: <20220708174133.2462418-1-polacek@redhat.com> <7955e63f-c20e-482c-cea3-db0942ed0149@redhat.com> <16983f2a-f56a-1462-c2db-3e4bc6fa6ad7@redhat.com> <93e09683-22c9-9e13-9ae1-13c4b447f38f@redhat.com> In-Reply-To: From: Ville Voutilainen Date: Fri, 15 Jul 2022 22:59:41 +0300 Message-ID: Subject: Re: [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] To: Marek Polacek Cc: Jason Merrill , Jonathan Wakely , "libstdc++" , GCC Patches X-Spam-Status: No, score=-7.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, 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 Content-Type: text/plain; charset="UTF-8" X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Jul 2022 20:00:05 -0000 Well, is_xible is not is_xible_p because it doesn't need to be both is_* and *_p. But xes_from_temporary is less obviously a question, so xes_from_temporary_p would imho be a better name. On Fri, Jul 15, 2022, 18:33 Marek Polacek via Libstdc++ < libstdc++@gcc.gnu.org> wrote: > On Thu, Jul 14, 2022 at 11:48:51PM -0400, Jason Merrill wrote: > > On 7/14/22 13:43, Marek Polacek wrote: > > > On Tue, Jul 12, 2022 at 04:15:00PM -0400, Jason Merrill wrote: > > > > On 7/12/22 16:10, Jason Merrill wrote: > > > > > On 7/8/22 13:41, Marek Polacek wrote: > > > > > > This patch implements C++23 P2255R2, which adds two new type > traits to > > > > > > detect reference binding to a temporary. They can be used to > detect code > > > > > > like > > > > > > > > > > > > std::tuple t("meow"); > > > > > > > > > > > > which is incorrect because it always creates a dangling > reference, > > > > > > because > > > > > > the std::string temporary is created inside the selected > constructor of > > > > > > std::tuple, and not outside it. > > > > > > > > > > > > There are two new compiler builtins, > > > > > > __reference_constructs_from_temporary > > > > > > and __reference_converts_from_temporary. The former is used to > simulate > > > > > > direct- and the latter copy-initialization context. But I had a > > > > > > hard time > > > > > > finding a test where there's actually a difference. Under DR > 2267, both > > > > > > of these are invalid: > > > > > > > > > > > > struct A { } a; > > > > > > struct B { explicit B(const A&); }; > > > > > > const B &b1{a}; > > > > > > const B &b2(a); > > > > > > > > > > > > so I had to peruse [over.match.ref], and eventually realized > that the > > > > > > difference can be seen here: > > > > > > > > > > > > struct G { > > > > > > operator int(); // #1 > > > > > > explicit operator int&&(); // #2 > > > > > > }; > > > > > > > > > > > > int&& r1(G{}); // use #2 (no temporary) > > > > > > int&& r2 = G{}; // use #1 (a temporary is created to be bound to > int&&) > > > > > > > > > > > > The implementation itself was rather straightforward because we > already > > > > > > have conv_binds_ref_to_prvalue. The main function here is > > > > > > reference_from_temporary. The renaming to > ref_conv_binds_to_temporary_p > > > > > > is because previously the function didn't distinguish between an > invalid > > > > > > conversion and one that binds to a prvalue. > > > > > > > > > > > > The patch also adds the relevant class and variable templates to > > > > > > . > > > > > > > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > > > > > > > > > PR c++/104477 > > > > > > > > > > > > gcc/c-family/ChangeLog: > > > > > > > > > > > > * c-common.cc (c_common_reswords): Add > > > > > > __reference_constructs_from_temporary and > > > > > > __reference_converts_from_temporary. > > > > > > * c-common.h (enum rid): Add > RID_REF_CONSTRUCTS_FROM_TEMPORARY and > > > > > > RID_REF_CONVERTS_FROM_TEMPORARY. > > > > > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > > > > > * call.cc (ref_conv_binds_directly_p): Rename to ... > > > > > > (ref_conv_binds_to_temporary_p): ... this. Add a new bool > > > > > > parameter. Return true only if the conversion is valid and > > > > > > conv_binds_ref_to_prvalue returns true. > > > > > > * constraint.cc (diagnose_trait_expr): Handle > > > > > > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and > > > > > > CPTK_REF_CONVERTS_FROM_TEMPORARY. > > > > > > * cp-tree.h (enum cp_trait_kind): Add > > > > > > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY > > > > > > and CPTK_REF_CONVERTS_FROM_TEMPORARY. > > > > > > (ref_conv_binds_directly_p): Rename to ... > > > > > > (ref_conv_binds_to_temporary_p): ... this. > > > > > > (reference_from_temporary): Declare. > > > > > > * cxx-pretty-print.cc (pp_cxx_trait_expression): Handle > > > > > > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and > > > > > > CPTK_REF_CONVERTS_FROM_TEMPORARY. > > > > > > * method.cc (reference_from_temporary): New. > > > > > > * parser.cc (cp_parser_primary_expression): Handle > > > > > > RID_REF_CONSTRUCTS_FROM_TEMPORARY and > > > > > > RID_REF_CONVERTS_FROM_TEMPORARY. > > > > > > (cp_parser_trait_expr): Likewise. > > > > > > (warn_for_range_copy): Adjust to call > ref_conv_binds_to_temporary_p. > > > > > > * semantics.cc (trait_expr_value): Handle > > > > > > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and > > > > > > CPTK_REF_CONVERTS_FROM_TEMPORARY. > > > > > > (finish_trait_expr): Likewise. > > > > > > > > > > > > libstdc++-v3/ChangeLog: > > > > > > > > > > > > * include/std/type_traits > (reference_constructs_from_temporary, > > > > > > reference_converts_from_temporary): New class templates. > > > > > > (reference_constructs_from_temporary_v, > > > > > > reference_converts_from_temporary_v): New variable > templates. > > > > > > (__cpp_lib_reference_from_temporary): Define for C++23. > > > > > > * include/std/version (__cpp_lib_reference_from_temporary): > > > > > > Define for > > > > > > C++23. > > > > > > * testsuite/20_util/variable_templates_for_traits.cc: Test > > > > > > reference_constructs_from_temporary_v and > > > > > > reference_converts_from_temporary_v. > > > > > > * testsuite/20_util/reference_from_temporary/value.cc: New > test. > > > > > > * testsuite/20_util/reference_from_temporary/value2.cc: New > test. > > > > > > * testsuite/20_util/reference_from_temporary/version.cc: > New test. > > > > > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > > > > > * g++.dg/ext/reference_constructs_from_temporary1.C: New > test. > > > > > > * g++.dg/ext/reference_converts_from_temporary1.C: New test. > > > > > > --- > > > > > > gcc/c-family/c-common.cc | 4 + > > > > > > gcc/c-family/c-common.h | 2 + > > > > > > gcc/cp/call.cc | 14 +- > > > > > > gcc/cp/constraint.cc | 8 + > > > > > > gcc/cp/cp-tree.h | 7 +- > > > > > > gcc/cp/cxx-pretty-print.cc | 6 + > > > > > > gcc/cp/method.cc | 28 +++ > > > > > > gcc/cp/parser.cc | 14 +- > > > > > > gcc/cp/semantics.cc | 8 + > > > > > > .../reference_constructs_from_temporary1.C | 214 > ++++++++++++++++++ > > > > > > .../ext/reference_converts_from_temporary1.C | 214 > ++++++++++++++++++ > > > > > > libstdc++-v3/include/std/type_traits | 39 ++++ > > > > > > libstdc++-v3/include/std/version | 5 +- > > > > > > .../20_util/reference_from_temporary/value.cc | 110 +++++++++ > > > > > > .../reference_from_temporary/value2.cc | 28 +++ > > > > > > .../reference_from_temporary/version.cc | 27 +++ > > > > > > .../20_util/variable_templates_for_traits.cc | 14 ++ > > > > > > 17 files changed, 730 insertions(+), 12 deletions(-) > > > > > > create mode 100644 > > > > > > gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C > > > > > > create mode 100644 > > > > > > gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C > > > > > > create mode 100644 > > > > > > libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc > > > > > > create mode 100644 > > > > > > libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc > > > > > > create mode 100644 > > > > > > > libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc > > > > > > > > > > > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > > > > > > index 1b8e73f7bc5..655c3aefee6 100644 > > > > > > --- a/gcc/c-family/c-common.cc > > > > > > +++ b/gcc/c-family/c-common.cc > > > > > > @@ -537,6 +537,10 @@ const struct c_common_resword > c_common_reswords[] = > > > > > > { "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY }, > > > > > > { "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, > D_CXXONLY }, > > > > > > { "__is_nothrow_constructible", > RID_IS_NOTHROW_CONSTRUCTIBLE, > > > > > > D_CXXONLY }, > > > > > > + { "__reference_constructs_from_temporary", > > > > > > RID_REF_CONSTRUCTS_FROM_TEMPORARY, > > > > > > + D_CXXONLY }, > > > > > > + { "__reference_converts_from_temporary", > > > > > > RID_REF_CONVERTS_FROM_TEMPORARY, > > > > > > + D_CXXONLY }, > > > > > > /* C++ transactional memory. */ > > > > > > { "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | > D_TRANSMEM }, > > > > > > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > > > > > > index c0900848965..f9064393b4e 100644 > > > > > > --- a/gcc/c-family/c-common.h > > > > > > +++ b/gcc/c-family/c-common.h > > > > > > @@ -184,6 +184,8 @@ enum rid > > > > > > RID_IS_UNION, RID_UNDERLYING_TYPE, > > > > > > RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE, > > > > > > RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE, > > > > > > + RID_REF_CONSTRUCTS_FROM_TEMPORARY, > > > > > > + RID_REF_CONVERTS_FROM_TEMPORARY, > > > > > > /* C++11 */ > > > > > > RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, > > > > > > RID_STATIC_ASSERT, > > > > > > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc > > > > > > index fc98552fda2..1ba209f61f1 100644 > > > > > > --- a/gcc/cp/call.cc > > > > > > +++ b/gcc/cp/call.cc > > > > > > @@ -9109,21 +9109,23 @@ conv_binds_ref_to_prvalue (conversion *c) > > > > > > return conv_is_prvalue (next_conversion (c)); > > > > > > } > > > > > > -/* True iff converting EXPR to a reference type TYPE does not > involve > > > > > > - creating a temporary. */ > > > > > > +/* True iff converting EXPR to a reference type TYPE binds the > > > > > > reference to > > > > > > + a temporary. DIRECT_INIT_P says whether the conversion > should > > > > > > be done > > > > > > + in direct- or copy-initialization context. */ > > > > > > bool > > > > > > -ref_conv_binds_directly_p (tree type, tree expr) > > > > > > +ref_conv_binds_to_temporary_p (tree type, tree expr, > > > > > > + bool direct_init_p /*= false*/) > > > > > > { > > > > > > gcc_assert (TYPE_REF_P (type)); > > > > > > /* Get the high-water mark for the CONVERSION_OBSTACK. */ > > > > > > void *p = conversion_obstack_alloc (0); > > > > > > + const int flags = direct_init_p ? LOOKUP_NORMAL : > LOOKUP_IMPLICIT; > > > > > > conversion *conv = implicit_conversion (type, TREE_TYPE > (expr), expr, > > > > > > - /*c_cast_p=*/false, > > > > > > - LOOKUP_IMPLICIT, tf_none); > > > > > > - bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue > (conv); > > > > > > + /*c_cast_p=*/false, flags, tf_none); > > > > > > + bool ret = conv && !conv->bad_p && conv_binds_ref_to_prvalue > (conv); > > > > > > /* Free all the conversions we allocated. */ > > > > > > obstack_free (&conversion_obstack, p); > > > > > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > > > > > > index 591155cee22..648cc9d176d 100644 > > > > > > --- a/gcc/cp/constraint.cc > > > > > > +++ b/gcc/cp/constraint.cc > > > > > > @@ -3687,6 +3687,14 @@ diagnose_trait_expr (tree expr, tree args) > > > > > > case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: > > > > > > inform (loc, " %qT does not have unique object > > > > > > representations", t1); > > > > > > break; > > > > > > + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: > > > > > > + inform (loc, " %qT is not a reference that binds to a > temporary " > > > > > > + "object of type %qT (direct-initialization)", t1, t2); > > > > > > + break; > > > > > > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > > > > > > + inform (loc, " %qT is not a reference that binds to a > temporary " > > > > > > + "object of type %qT (copy-initialization)", t1, t2); > > > > > > + break; > > > > > > case CPTK_BASES: > > > > > > case CPTK_DIRECT_BASES: > > > > > > case CPTK_UNDERLYING_TYPE: > > > > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > > > > > > index 2fde4f83b41..c3bed31e455 100644 > > > > > > --- a/gcc/cp/cp-tree.h > > > > > > +++ b/gcc/cp/cp-tree.h > > > > > > @@ -1397,7 +1397,9 @@ enum cp_trait_kind > > > > > > CPTK_IS_ASSIGNABLE, > > > > > > CPTK_IS_CONSTRUCTIBLE, > > > > > > CPTK_IS_NOTHROW_ASSIGNABLE, > > > > > > - CPTK_IS_NOTHROW_CONSTRUCTIBLE > > > > > > + CPTK_IS_NOTHROW_CONSTRUCTIBLE, > > > > > > + CPTK_REF_CONSTRUCTS_FROM_TEMPORARY, > > > > > > + CPTK_REF_CONVERTS_FROM_TEMPORARY > > > > > > }; > > > > > > /* The types that we are processing. */ > > > > > > @@ -6520,7 +6522,7 @@ extern bool sufficient_parms_p > > > > > > (const_tree); > > > > > > extern tree type_decays_to (tree); > > > > > > extern tree extract_call_expr (tree); > > > > > > extern tree build_trivial_dtor_call (tree, bool = > false); > > > > > > -extern bool ref_conv_binds_directly_p (tree, tree); > > > > > > +extern bool ref_conv_binds_to_temporary_p (tree, tree, bool > = false); > > > > > > extern tree build_user_type_conversion (tree, tree, > int, > > > > > > tsubst_flags_t); > > > > > > extern tree build_new_function_call (tree, vec va_gc> **, > > > > > > @@ -7105,6 +7107,7 @@ extern tree forward_parm (tree); > > > > > > extern bool is_trivially_xible (enum tree_code, > tree, tree); > > > > > > extern bool is_nothrow_xible (enum tree_code, > tree, tree); > > > > > > extern bool is_xible (enum tree_code, tree, > tree); > > > > > > +extern bool reference_from_temporary (tree, tree, bool); > > > > > > extern tree get_defaulted_eh_spec (tree, > tsubst_flags_t = > > > > > > tf_warning_or_error); > > > > > > extern bool maybe_explain_implicit_delete (tree); > > > > > > extern void explain_implicit_non_constexpr (tree); > > > > > > diff --git a/gcc/cp/cxx-pretty-print.cc > b/gcc/cp/cxx-pretty-print.cc > > > > > > index 7e4db2e413b..44590830a61 100644 > > > > > > --- a/gcc/cp/cxx-pretty-print.cc > > > > > > +++ b/gcc/cp/cxx-pretty-print.cc > > > > > > @@ -2696,6 +2696,12 @@ pp_cxx_trait_expression > (cxx_pretty_printer > > > > > > *pp, tree t) > > > > > > case CPTK_IS_NOTHROW_CONSTRUCTIBLE: > > > > > > pp_cxx_ws_string (pp, "__is_nothrow_constructible"); > > > > > > break; > > > > > > + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: > > > > > > + pp_cxx_ws_string (pp, > "__reference_constructs_from_temporary"); > > > > > > + break; > > > > > > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > > > > > > + pp_cxx_ws_string (pp, > "__reference_converts_from_temporary"); > > > > > > + break; > > > > > > default: > > > > > > gcc_unreachable (); > > > > > > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc > > > > > > index 0dffd648b0b..dd9715b6725 100644 > > > > > > --- a/gcc/cp/method.cc > > > > > > +++ b/gcc/cp/method.cc > > > > > > @@ -2211,6 +2211,34 @@ is_xible (enum tree_code code, tree to, > tree from) > > > > > > return !!expr; > > > > > > } > > > > > > +/* Return true iff conjunction_v, > > > > > > is_constructible> is > > > > > > + true, and the initialization > > > > > > + T t(VAL); // DIRECT_INIT_P > > > > > > + or > > > > > > + T t = VAL; // !DIRECT_INIT_P > > > > > > + binds t to a temporary object whose lifetime is extended. > > > > > > + VAL is defined in [meta.unary.prop]: > > > > > > + -- If T is a reference or function type, VAL is an > expression > > > > > > with the > > > > > > + same type and value category as declval(). > > > > > > + -- Otherwise, VAL is a prvalue that initially has type > T. */ > > > > > > + > > > > > > +bool > > > > > > +reference_from_temporary (tree to, tree from, bool > direct_init_p) > > > > > > +{ > > > > > > + /* Check is_reference. */ > > > > > > + if (!TYPE_REF_P (to)) > > > > > > + return false; > > > > > > + /* Check is_constructible. > > > > > > + ??? This check doesn't seem to be necessary; if T isn't > > > > > > constructible > > > > > > + from U, we won't be able to create a conversion. */ > > > > > > + if (!is_xible (INIT_EXPR, to, build_tree_list (NULL_TREE, > from))) > > > > > > + return false; > > > > > > > > > > I agree with the comment, did you try leaving this out? If it > stays I'd > > > > > think it needs to consider direct_init_p. > > > > > > > > > > > + tree val = build_stub_object (from); > > > > > > + if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE) > > > > > > + val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) : > > > > > > rvalue (val); > > > > > > + return ref_conv_binds_to_temporary_p (to, val, direct_init_p); > > > > > > +} > > > > > > + > > > > > > /* Categorize various special_function_kinds. */ > > > > > > #define SFK_CTOR_P(sfk) \ > > > > > > ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor) > > > > > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > > > > > > index bf9ea3685f8..edee94bda13 100644 > > > > > > --- a/gcc/cp/parser.cc > > > > > > +++ b/gcc/cp/parser.cc > > > > > > @@ -5917,6 +5917,8 @@ cp_parser_primary_expression (cp_parser > *parser, > > > > > > case RID_IS_CONSTRUCTIBLE: > > > > > > case RID_IS_NOTHROW_ASSIGNABLE: > > > > > > case RID_IS_NOTHROW_CONSTRUCTIBLE: > > > > > > + case RID_REF_CONSTRUCTS_FROM_TEMPORARY: > > > > > > + case RID_REF_CONVERTS_FROM_TEMPORARY: > > > > > > return cp_parser_trait_expr (parser, token->keyword); > > > > > > // C++ concepts > > > > > > @@ -10988,6 +10990,14 @@ cp_parser_trait_expr (cp_parser* parser, > > > > > > enum rid keyword) > > > > > > kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE; > > > > > > variadic = true; > > > > > > break; > > > > > > + case RID_REF_CONSTRUCTS_FROM_TEMPORARY: > > > > > > + kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY; > > > > > > + binary = true; > > > > > > + break; > > > > > > + case RID_REF_CONVERTS_FROM_TEMPORARY: > > > > > > + kind = CPTK_REF_CONVERTS_FROM_TEMPORARY; > > > > > > + binary = true; > > > > > > + break; > > > > > > default: > > > > > > gcc_unreachable (); > > > > > > } > > > > > > @@ -13811,7 +13821,7 @@ warn_for_range_copy (tree decl, tree > expr) > > > > > > if (TYPE_REF_P (type)) > > > > > > { > > > > > > - if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, > expr)) > > > > > > + if (glvalue_p (expr) && ref_conv_binds_to_temporary_p > (type, > > > > > > expr)) > > > > > > { > > > > > > auto_diagnostic_group d; > > > > > > if (warning_at (loc, OPT_Wrange_loop_construct, > > > > > > @@ -13842,7 +13852,7 @@ warn_for_range_copy (tree decl, tree > expr) > > > > > > tree rtype = cp_build_reference_type (type, /*rval*/false); > > > > > > /* If we could initialize the reference directly, it > wouldn't > > > > > > involve any > > > > > > copies. */ > > > > > > - if (!ref_conv_binds_directly_p (rtype, expr)) > > > > > > + if (ref_conv_binds_to_temporary_p (rtype, expr)) > > > > > > return; > > > > > > > > > > I think this case wants the old handling of invalid conversions you > > > > > mentioned in your intro; we don't want to suggest changing to a > > > > > reference if that's ill-formed. > > > > > > > > > > In passing we might change the comment to "If we can initialize a > > > > > reference directly, suggest that to avoid the copy." and move it > above > > > > > the rtype declaration. > > > > > > > > Hmm, and I suspect we get false positives when expr is a prvalue, so > > > > initializing a non-reference variable of the same cv-unqualified type > > > > involves no extra copy? > > > > > > I couldn't provoke a false positive here. Note that > ref_conv_binds_to_temporary_p > > > always gets a reference so copy elision isn't applicable here. > > > > Ah, I see: if expr is a prvalue, that counts as binding a reference to a > > temporary, so we (properly) don't think the reference is an improvement. > > > > The function name reference_from_temporary seems to me to suggest a tree > > value rather than bool, maybe rename to ref_xes_from_temporary? > > I agree that that's a better name, so I've changed it. I would've > preferred > ref_xes_from_temporary_p but is_xible et al don't have the _p. > > I'm going to push this, thanks! > > Tested x86_64-pc-linux-gnu, applying to trunk. > > -- >8 -- > This patch implements C++23 P2255R2, which adds two new type traits to > detect reference binding to a temporary. They can be used to detect code > like > > std::tuple t("meow"); > > which is incorrect because it always creates a dangling reference, because > the std::string temporary is created inside the selected constructor of > std::tuple, and not outside it. > > There are two new compiler builtins, __reference_constructs_from_temporary > and __reference_converts_from_temporary. The former is used to simulate > direct- and the latter copy-initialization context. But I had a hard time > finding a test where there's actually a difference. Under DR 2267, both > of these are invalid: > > struct A { } a; > struct B { explicit B(const A&); }; > const B &b1{a}; > const B &b2(a); > > so I had to peruse [over.match.ref], and eventually realized that the > difference can be seen here: > > struct G { > operator int(); // #1 > explicit operator int&&(); // #2 > }; > > int&& r1(G{}); // use #2 (no temporary) > int&& r2 = G{}; // use #1 (a temporary is created to be bound to int&&) > > The implementation itself was rather straightforward because we already > have the conv_binds_ref_to_prvalue function. The main function here is > ref_xes_from_temporary. > I've changed the return type of ref_conv_binds_directly to tristate, > because > previously the function didn't distinguish between an invalid conversion > and > one that binds to a prvalue. Since it no longer returns a bool, I removed > the _p suffix. > > The patch also adds the relevant class and variable templates to > . > > PR c++/104477 > > gcc/c-family/ChangeLog: > > * c-common.cc (c_common_reswords): Add > __reference_constructs_from_temporary and > __reference_converts_from_temporary. > * c-common.h (enum rid): Add RID_REF_CONSTRUCTS_FROM_TEMPORARY and > RID_REF_CONVERTS_FROM_TEMPORARY. > > gcc/cp/ChangeLog: > > * call.cc (ref_conv_binds_directly_p): Rename to ... > (ref_conv_binds_directly): ... this. Add a new bool parameter. > Change > the return type to tristate. > * constraint.cc (diagnose_trait_expr): Handle > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and > CPTK_REF_CONVERTS_FROM_TEMPORARY. > * cp-tree.h: Include "tristate.h". > (enum cp_trait_kind): Add CPTK_REF_CONSTRUCTS_FROM_TEMPORARY > and CPTK_REF_CONVERTS_FROM_TEMPORARY. > (ref_conv_binds_directly_p): Rename to ... > (ref_conv_binds_directly): ... this. > (ref_xes_from_temporary): Declare. > * cxx-pretty-print.cc (pp_cxx_trait_expression): Handle > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and > CPTK_REF_CONVERTS_FROM_TEMPORARY. > * method.cc (ref_xes_from_temporary): New. > * parser.cc (cp_parser_primary_expression): Handle > RID_REF_CONSTRUCTS_FROM_TEMPORARY and > RID_REF_CONVERTS_FROM_TEMPORARY. > (cp_parser_trait_expr): Likewise. > (warn_for_range_copy): Adjust to call ref_conv_binds_directly. > * semantics.cc (trait_expr_value): Handle > CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and > CPTK_REF_CONVERTS_FROM_TEMPORARY. > (finish_trait_expr): Likewise. > > libstdc++-v3/ChangeLog: > > * include/std/type_traits (reference_constructs_from_temporary, > reference_converts_from_temporary): New class templates. > (reference_constructs_from_temporary_v, > reference_converts_from_temporary_v): New variable templates. > (__cpp_lib_reference_from_temporary): Define for C++23. > * include/std/version (__cpp_lib_reference_from_temporary): Define > for > C++23. > * testsuite/20_util/variable_templates_for_traits.cc: Test > reference_constructs_from_temporary_v and > reference_converts_from_temporary_v. > * testsuite/20_util/reference_from_temporary/value.cc: New test. > * testsuite/20_util/reference_from_temporary/value2.cc: New test. > * testsuite/20_util/reference_from_temporary/version.cc: New test. > > gcc/testsuite/ChangeLog: > > * g++.dg/ext/reference_constructs_from_temporary1.C: New test. > * g++.dg/ext/reference_converts_from_temporary1.C: New test. > --- > gcc/c-family/c-common.cc | 4 + > gcc/c-family/c-common.h | 2 + > gcc/cp/call.cc | 20 +- > gcc/cp/constraint.cc | 8 + > gcc/cp/cp-tree.h | 8 +- > gcc/cp/cxx-pretty-print.cc | 6 + > gcc/cp/method.cc | 25 ++ > gcc/cp/parser.cc | 36 +-- > gcc/cp/semantics.cc | 8 + > .../reference_constructs_from_temporary1.C | 214 ++++++++++++++++++ > .../ext/reference_converts_from_temporary1.C | 214 ++++++++++++++++++ > libstdc++-v3/include/std/type_traits | 39 ++++ > libstdc++-v3/include/std/version | 5 +- > .../20_util/reference_from_temporary/value.cc | 110 +++++++++ > .../reference_from_temporary/value2.cc | 28 +++ > .../reference_from_temporary/version.cc | 27 +++ > .../20_util/variable_templates_for_traits.cc | 14 ++ > 17 files changed, 744 insertions(+), 24 deletions(-) > create mode 100644 > gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C > create mode 100644 > gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C > create mode 100644 > libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 1b8e73f7bc5..655c3aefee6 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -537,6 +537,10 @@ const struct c_common_resword c_common_reswords[] = > { "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY }, > { "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY }, > { "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, D_CXXONLY > }, > + { "__reference_constructs_from_temporary", > RID_REF_CONSTRUCTS_FROM_TEMPORARY, > + D_CXXONLY }, > + { "__reference_converts_from_temporary", > RID_REF_CONVERTS_FROM_TEMPORARY, > + D_CXXONLY }, > > /* C++ transactional memory. */ > { "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM }, > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index c0900848965..f9064393b4e 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -184,6 +184,8 @@ enum rid > RID_IS_UNION, RID_UNDERLYING_TYPE, > RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE, > RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE, > + RID_REF_CONSTRUCTS_FROM_TEMPORARY, > + RID_REF_CONVERTS_FROM_TEMPORARY, > > /* C++11 */ > RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, > RID_STATIC_ASSERT, > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc > index fc98552fda2..191c68cdcfd 100644 > --- a/gcc/cp/call.cc > +++ b/gcc/cp/call.cc > @@ -9109,21 +9109,27 @@ conv_binds_ref_to_prvalue (conversion *c) > return conv_is_prvalue (next_conversion (c)); > } > > -/* True iff converting EXPR to a reference type TYPE does not involve > - creating a temporary. */ > +/* Return tristate::TS_TRUE if converting EXPR to a reference type TYPE > does > + not involve creating a temporary. Return tristate::TS_FALSE if > converting > + EXPR to a reference type TYPE binds the reference to a temporary. If > the > + conversion is invalid or bad, return tristate::TS_UNKNOWN. > DIRECT_INIT_P > + says whether the conversion should be done in direct- or > copy-initialization > + context. */ > > -bool > -ref_conv_binds_directly_p (tree type, tree expr) > +tristate > +ref_conv_binds_directly (tree type, tree expr, bool direct_init_p /*= > false*/) > { > gcc_assert (TYPE_REF_P (type)); > > /* Get the high-water mark for the CONVERSION_OBSTACK. */ > void *p = conversion_obstack_alloc (0); > > + const int flags = direct_init_p ? LOOKUP_NORMAL : LOOKUP_IMPLICIT; > conversion *conv = implicit_conversion (type, TREE_TYPE (expr), expr, > - /*c_cast_p=*/false, > - LOOKUP_IMPLICIT, tf_none); > - bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue (conv); > + /*c_cast_p=*/false, flags, > tf_none); > + tristate ret (tristate::TS_UNKNOWN); > + if (conv && !conv->bad_p) > + ret = tristate (!conv_binds_ref_to_prvalue (conv)); > > /* Free all the conversions we allocated. */ > obstack_free (&conversion_obstack, p); > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index f2137eb7aa1..568318f0ba1 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -3697,6 +3697,14 @@ diagnose_trait_expr (tree expr, tree args) > case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: > inform (loc, " %qT does not have unique object representations", > t1); > break; > + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: > + inform (loc, " %qT is not a reference that binds to a temporary " > + "object of type %qT (direct-initialization)", t1, t2); > + break; > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > + inform (loc, " %qT is not a reference that binds to a temporary " > + "object of type %qT (copy-initialization)", t1, t2); > + break; > case CPTK_BASES: > case CPTK_DIRECT_BASES: > case CPTK_UNDERLYING_TYPE: > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index bec98aa2ac3..cf51c39dc90 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see > #include "tm.h" > #include "hard-reg-set.h" > #include "function.h" > +#include "tristate.h" > > /* In order for the format checking to accept the C++ front end > diagnostic framework extensions, you must include this file before > @@ -1397,7 +1398,9 @@ enum cp_trait_kind > CPTK_IS_ASSIGNABLE, > CPTK_IS_CONSTRUCTIBLE, > CPTK_IS_NOTHROW_ASSIGNABLE, > - CPTK_IS_NOTHROW_CONSTRUCTIBLE > + CPTK_IS_NOTHROW_CONSTRUCTIBLE, > + CPTK_REF_CONSTRUCTS_FROM_TEMPORARY, > + CPTK_REF_CONVERTS_FROM_TEMPORARY > }; > > /* The types that we are processing. */ > @@ -6520,7 +6523,7 @@ extern bool sufficient_parms_p > (const_tree); > extern tree type_decays_to (tree); > extern tree extract_call_expr (tree); > extern tree build_trivial_dtor_call (tree, bool = false); > -extern bool ref_conv_binds_directly_p (tree, tree); > +extern tristate ref_conv_binds_directly (tree, tree, bool > = false); > extern tree build_user_type_conversion (tree, tree, int, > tsubst_flags_t); > extern tree build_new_function_call (tree, vec **, > @@ -7105,6 +7108,7 @@ extern tree forward_parm (tree); > extern bool is_trivially_xible (enum tree_code, tree, > tree); > extern bool is_nothrow_xible (enum tree_code, tree, > tree); > extern bool is_xible (enum tree_code, tree, > tree); > +extern bool ref_xes_from_temporary (tree, tree, bool); > extern tree get_defaulted_eh_spec (tree, tsubst_flags_t = > tf_warning_or_error); > extern bool maybe_explain_implicit_delete (tree); > extern void explain_implicit_non_constexpr (tree); > diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc > index 7e4db2e413b..44590830a61 100644 > --- a/gcc/cp/cxx-pretty-print.cc > +++ b/gcc/cp/cxx-pretty-print.cc > @@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, > tree t) > case CPTK_IS_NOTHROW_CONSTRUCTIBLE: > pp_cxx_ws_string (pp, "__is_nothrow_constructible"); > break; > + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: > + pp_cxx_ws_string (pp, "__reference_constructs_from_temporary"); > + break; > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > + pp_cxx_ws_string (pp, "__reference_converts_from_temporary"); > + break; > > default: > gcc_unreachable (); > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc > index 0dffd648b0b..f2050f6e970 100644 > --- a/gcc/cp/method.cc > +++ b/gcc/cp/method.cc > @@ -2211,6 +2211,31 @@ is_xible (enum tree_code code, tree to, tree from) > return !!expr; > } > > +/* Return true iff conjunction_v, is_constructible> > is > + true, and the initialization > + T t(VAL); // DIRECT_INIT_P > + or > + T t = VAL; // !DIRECT_INIT_P > + binds t to a temporary object whose lifetime is extended. > + VAL is defined in [meta.unary.prop]: > + -- If T is a reference or function type, VAL is an expression with > the > + same type and value category as declval(). > + -- Otherwise, VAL is a prvalue that initially has type T. */ > + > +bool > +ref_xes_from_temporary (tree to, tree from, bool direct_init_p) > +{ > + /* Check is_reference. */ > + if (!TYPE_REF_P (to)) > + return false; > + /* We don't check is_constructible: if T isn't constructible > + from U, we won't be able to create a conversion. */ > + tree val = build_stub_object (from); > + if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE) > + val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) : rvalue > (val); > + return ref_conv_binds_directly (to, val, direct_init_p).is_false (); > +} > + > /* Categorize various special_function_kinds. */ > #define SFK_CTOR_P(sfk) \ > ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor) > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index bf9ea3685f8..4f67441eeb1 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -5917,6 +5917,8 @@ cp_parser_primary_expression (cp_parser *parser, > case RID_IS_CONSTRUCTIBLE: > case RID_IS_NOTHROW_ASSIGNABLE: > case RID_IS_NOTHROW_CONSTRUCTIBLE: > + case RID_REF_CONSTRUCTS_FROM_TEMPORARY: > + case RID_REF_CONVERTS_FROM_TEMPORARY: > return cp_parser_trait_expr (parser, token->keyword); > > // C++ concepts > @@ -10988,6 +10990,14 @@ cp_parser_trait_expr (cp_parser* parser, enum rid > keyword) > kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE; > variadic = true; > break; > + case RID_REF_CONSTRUCTS_FROM_TEMPORARY: > + kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY; > + binary = true; > + break; > + case RID_REF_CONVERTS_FROM_TEMPORARY: > + kind = CPTK_REF_CONVERTS_FROM_TEMPORARY; > + binary = true; > + break; > default: > gcc_unreachable (); > } > @@ -13811,7 +13821,7 @@ warn_for_range_copy (tree decl, tree expr) > > if (TYPE_REF_P (type)) > { > - if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, expr)) > + if (glvalue_p (expr) && ref_conv_binds_directly (type, > expr).is_false ()) > { > auto_diagnostic_group d; > if (warning_at (loc, OPT_Wrange_loop_construct, > @@ -13839,20 +13849,20 @@ warn_for_range_copy (tree decl, tree expr) > && trivially_copyable_p (type))) > return; > > + /* If we can initialize a reference directly, suggest that to avoid the > + copy. */ > tree rtype = cp_build_reference_type (type, /*rval*/false); > - /* If we could initialize the reference directly, it wouldn't involve > any > - copies. */ > - if (!ref_conv_binds_directly_p (rtype, expr)) > - return; > - > - auto_diagnostic_group d; > - if (warning_at (loc, OPT_Wrange_loop_construct, > - "loop variable %qD creates a copy from type %qT", > - decl, type)) > + if (ref_conv_binds_directly (rtype, expr).is_true ()) > { > - gcc_rich_location richloc (loc); > - richloc.add_fixit_insert_before ("&"); > - inform (&richloc, "use reference type to prevent copying"); > + auto_diagnostic_group d; > + if (warning_at (loc, OPT_Wrange_loop_construct, > + "loop variable %qD creates a copy from type %qT", > + decl, type)) > + { > + gcc_rich_location richloc (loc); > + richloc.add_fixit_insert_before ("&"); > + inform (&richloc, "use reference type to prevent copying"); > + } > } > } > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > index 2344b5eea00..96037c21b85 100644 > --- a/gcc/cp/semantics.cc > +++ b/gcc/cp/semantics.cc > @@ -12007,6 +12007,12 @@ trait_expr_value (cp_trait_kind kind, tree type1, > tree type2) > case CPTK_IS_NOTHROW_CONSTRUCTIBLE: > return is_nothrow_xible (INIT_EXPR, type1, type2); > > + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: > + return ref_xes_from_temporary (type1, type2, /*direct_init=*/true); > + > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > + return ref_xes_from_temporary (type1, type2, /*direct_init=*/false); > + > default: > gcc_unreachable (); > return false; > @@ -12088,6 +12094,8 @@ finish_trait_expr (location_t loc, cp_trait_kind > kind, tree type1, tree type2) > case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE: > case CPTK_IS_NOTHROW_ASSIGNABLE: > case CPTK_IS_NOTHROW_CONSTRUCTIBLE: > + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > if (!check_trait_type (type1) > || !check_trait_type (type2)) > return error_mark_node; > diff --git > a/gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C > b/gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C > new file mode 100644 > index 00000000000..76de905a35d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C > @@ -0,0 +1,214 @@ > +// P2255R2 > +// PR c++/104477 > +// { dg-do compile { target c++11 } } > + > +#define SA(X) static_assert((X),#X) > + > +struct A { A(); }; > +struct B { operator A(); }; > +struct C { explicit C(); }; > +struct D { explicit operator A(); }; > +struct E { explicit operator A&(); }; > +struct F { explicit operator A&&(); }; > +// Could use a class template with explicit(bool), but then this would > need > +// C++20. > +struct G { > + operator int(); > + explicit operator int&&(); > +}; > +struct G2 { > + explicit operator int(); > + operator int&&(); > +}; > +struct H { > + operator int(); > + explicit operator int&(); > +}; > +struct H2 { > + explicit operator int(); > + operator int&(); > +}; > + > +struct Base { }; > +struct Der : Base { }; > + > +template > +struct morph { > + mutable T val{}; > + operator RT() const { return static_cast(val); } > +}; > + > +template using id = T; > + > +// Built-in types. > +SA(!__reference_constructs_from_temporary(int, int)); > +SA(!__reference_constructs_from_temporary(int&, void)); > +SA(!__reference_constructs_from_temporary(int&, const void)); > +SA(!__reference_constructs_from_temporary(int&, volatile void)); > +SA(!__reference_constructs_from_temporary(int&, const volatile void)); > +SA(!__reference_constructs_from_temporary(void, void)); > +SA(!__reference_constructs_from_temporary(void, int)); > +SA(!__reference_constructs_from_temporary(int&, int)); > +SA(!__reference_constructs_from_temporary(int&, int&)); > +SA(!__reference_constructs_from_temporary(int&, int&&)); > +SA(!__reference_constructs_from_temporary(int&, long)); > +// non-const lvalue reference to type 'int' cannot bind to a value of > unrelated type 'long' > +SA(!__reference_constructs_from_temporary(int&, long&)); > +SA(!__reference_constructs_from_temporary(int&, long&&)); > +SA( __reference_constructs_from_temporary(const int&, int)); > +SA(!__reference_constructs_from_temporary(const int&, int&)); > +SA(!__reference_constructs_from_temporary(const int&, const int&)); > +SA(!__reference_constructs_from_temporary(const int&, int&&)); > +SA( __reference_constructs_from_temporary(const int&, long)); > +SA( __reference_constructs_from_temporary(const int&, long&)); > +SA( __reference_constructs_from_temporary(const int&, long&&)); > +SA( __reference_constructs_from_temporary(int&&, int)); > +SA(!__reference_constructs_from_temporary(int&&, int&)); > +SA(!__reference_constructs_from_temporary(int&&, int&&)); > +SA( __reference_constructs_from_temporary(int&&, long)); > +SA( __reference_constructs_from_temporary(int&&, long&)); > +SA( __reference_constructs_from_temporary(int&&, long&&)); > +SA(!__reference_constructs_from_temporary(unsigned int&, double)); > +SA(!__reference_constructs_from_temporary(volatile int&, int)); > +SA(!__reference_constructs_from_temporary(const volatile int&, int)); > +SA(!__reference_constructs_from_temporary(volatile int&, int&)); > +SA(!__reference_constructs_from_temporary(const volatile int&, int&)); > +SA(!__reference_constructs_from_temporary(volatile int&, int&&)); > +SA(!__reference_constructs_from_temporary(const volatile int&, int&&)); > + > +// Classes. > +SA(!__reference_constructs_from_temporary(A, A)); > +// A& r(A{}); doesn't construct. > +SA(!__reference_constructs_from_temporary(A&, A)); > +SA(!__reference_constructs_from_temporary(A&, A&)); > +SA(!__reference_constructs_from_temporary(A&, A&&)); > +// Here we get const struct A & r = (const struct A &) &D.2414; > +SA( __reference_constructs_from_temporary(const A&, A)); > +SA(!__reference_constructs_from_temporary(const A&, A&)); > +SA(!__reference_constructs_from_temporary(const A&, const A&)); > +SA(!__reference_constructs_from_temporary(const A&, A&&)); > +// Here we get struct A & r = (struct A &) &D.2439; > +SA( __reference_constructs_from_temporary(A&&, A)); > +SA(!__reference_constructs_from_temporary(A&&, A&)); > +SA(!__reference_constructs_from_temporary(A&&, const A&)); > +SA(!__reference_constructs_from_temporary(A&&, A&&)); > + > +SA(!__reference_constructs_from_temporary(A, B)); > +SA(!__reference_constructs_from_temporary(A&, B)); > +SA(!__reference_constructs_from_temporary(A&, B&)); > +SA(!__reference_constructs_from_temporary(A&, const B&)); > +SA(!__reference_constructs_from_temporary(A&, B&&)); > +SA( __reference_constructs_from_temporary(const A&, B)); > +SA( __reference_constructs_from_temporary(const A&, B&)); > +// Doesn't construct, so it's false. > +SA(!__reference_constructs_from_temporary(const A&, const B&)); > +SA( __reference_constructs_from_temporary(const A&, B&&)); > +SA( __reference_constructs_from_temporary(A&&, B)); > +SA( __reference_constructs_from_temporary(A&&, B&)); > +SA(!__reference_constructs_from_temporary(A&&, const B&)); > +SA( __reference_constructs_from_temporary(A&&, B&&)); > + > +SA(!__reference_constructs_from_temporary(const A&, C)); > +SA(!__reference_constructs_from_temporary(const A&, C&)); > + > +// Doesn't construct, so it's false. > +SA(!__reference_constructs_from_temporary(int&, morph)); > +// Here we get > +// const int & r2 = D.2580 = morph::operator int > +// (&TARGET_EXPR ); (const int &) &D.2580; > +SA( __reference_constructs_from_temporary(const int&, morph)); > +SA(!__reference_constructs_from_temporary(int&, morph)); > +SA(!__reference_constructs_from_temporary(int&, morph)); > +SA(!__reference_constructs_from_temporary(int&, morph)); > +SA( __reference_constructs_from_temporary(const int&, morph long&>)); > + > +// These are like const C& c(cref); so the explicit ctor C isn't a problem > +// even in copy-init context. const C& r = {}; would be a different > story. > +SA(!__reference_constructs_from_temporary(C, C)); > +SA(!__reference_constructs_from_temporary(C&, C)); > +SA(!__reference_constructs_from_temporary(C&, C&)); > +SA(!__reference_constructs_from_temporary(C&, C&&)); > +SA( __reference_constructs_from_temporary(const C&, C)); > +SA(!__reference_constructs_from_temporary(const C&, C&)); > +SA(!__reference_constructs_from_temporary(const C&, const C&)); > +SA(!__reference_constructs_from_temporary(const C&, C&&)); > +SA( __reference_constructs_from_temporary(C&&, C)); > +SA(!__reference_constructs_from_temporary(C&&, C&)); > +SA(!__reference_constructs_from_temporary(C&&, const C&)); > +SA(!__reference_constructs_from_temporary(C&&, C&&)); > + > +// These are all false ultimately because of CWG 2267, which we implement. > +SA(!__reference_constructs_from_temporary(A, D)); > +SA(!__reference_constructs_from_temporary(A&, D)); > +SA(!__reference_constructs_from_temporary(A&, D&)); > +SA(!__reference_constructs_from_temporary(A&, const D&)); > +SA(!__reference_constructs_from_temporary(A&, D&&)); > +SA(!__reference_constructs_from_temporary(const A&, D)); > +SA(!__reference_constructs_from_temporary(const A&, D&)); > +SA(!__reference_constructs_from_temporary(const A&, const D&)); > +SA(!__reference_constructs_from_temporary(const A&, D&&)); > +SA(!__reference_constructs_from_temporary(A&&, D)); > +SA(!__reference_constructs_from_temporary(A&&, D&)); > +SA(!__reference_constructs_from_temporary(A&&, const D&)); > +SA(!__reference_constructs_from_temporary(A&&, D&&)); > + > +SA(!__reference_constructs_from_temporary(A, E)); > +/* A& a1(E{}); compiles, but A& a2 = E{}; doesn't. > + With the former, we get A& a = E::operator A& (&TARGET_EXPR {}>) > + so we're not binding the reference to a temporary, although there is > + a temporary involved. So the result is false in both copy- and direct- > + init, albeit for different reasons! */ > +SA(!__reference_constructs_from_temporary(A&, E)); > +// A& a = E::operator A& ((struct E *) r)); copy-init doesn't compile. > +SA(!__reference_constructs_from_temporary(A&, E&)); > +SA(!__reference_constructs_from_temporary(A&, const E&)); > +SA(!__reference_constructs_from_temporary(A&, E&&)); > +// direct-init: > +// const A& a = (const struct A &) E::operator A& (&TARGET_EXPR {}>) > +SA(!__reference_constructs_from_temporary(const A&, E)); > +SA(!__reference_constructs_from_temporary(const A&, E&)); > +SA(!__reference_constructs_from_temporary(const A&, const E&)); > +SA(!__reference_constructs_from_temporary(const A&, E&&)); > +SA(!__reference_constructs_from_temporary(A&&, E)); > +SA(!__reference_constructs_from_temporary(A&&, E&)); > +SA(!__reference_constructs_from_temporary(A&&, const E&)); > +SA(!__reference_constructs_from_temporary(A&&, E&&)); > + > +SA(!__reference_constructs_from_temporary(A, F)); > +// A& a1(F{}); and A& a2 = F{}; both invalid. > +SA(!__reference_constructs_from_temporary(A&, F)); > +SA(!__reference_constructs_from_temporary(A&, F&)); > +SA(!__reference_constructs_from_temporary(A&, const F&)); > +SA(!__reference_constructs_from_temporary(A&, F&&)); > +SA(!__reference_constructs_from_temporary(const A&, F)); > +SA(!__reference_constructs_from_temporary(const A&, F&)); > +SA(!__reference_constructs_from_temporary(const A&, const F&)); > +SA(!__reference_constructs_from_temporary(const A&, F&&)); > +SA(!__reference_constructs_from_temporary(A&&, F)); > +SA(!__reference_constructs_from_temporary(A&&, F&)); > +SA(!__reference_constructs_from_temporary(A&&, const F&)); > +SA(!__reference_constructs_from_temporary(A&&, F&&)); > + > +/* This is where _converts_ and _constructs_ will differ: > + in direct-init we use G::operator int&& (no temporary), > + but in copy-init we use G::operator int, where a temporary is created > + to be bound to int&&. */ > +SA(!__reference_constructs_from_temporary(int&&, G)); > +// Similar to the previous one. > +SA(!__reference_constructs_from_temporary(const int&, H)); > +/* And here I've switched the explicit-ness. In both copy- and > direct-init > + we call operator int&, so no temporary. */ > +SA(!__reference_constructs_from_temporary(int&&, G2)); > +SA(!__reference_constructs_from_temporary(const int&, H2)); > + > +SA(!__reference_constructs_from_temporary(const Base&, Der)); > + > +// This fails because std::is_constructible_v> is false. > +SA(!__reference_constructs_from_temporary(int&&, id)); > + > +// Arrays. > +SA(!__reference_constructs_from_temporary(int, int[])); > +SA(!__reference_constructs_from_temporary(int[], int[])); > +SA(!__reference_constructs_from_temporary(int&, int[])); > +SA(!__reference_constructs_from_temporary(int&&, int[])); > +SA(!__reference_constructs_from_temporary(const int&, int[])); > diff --git a/gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C > b/gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C > new file mode 100644 > index 00000000000..90196c38742 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C > @@ -0,0 +1,214 @@ > +// P2255R2 > +// PR c++/104477 > +// { dg-do compile { target c++11 } } > + > +#define SA(X) static_assert((X),#X) > + > +struct A { A(); }; > +struct B { operator A(); }; > +struct C { explicit C(); }; > +struct D { explicit operator A(); }; > +struct E { explicit operator A&(); }; > +struct F { explicit operator A&&(); }; > +// Could use a class template with explicit(bool), but then this would > need > +// C++20. > +struct G { > + operator int(); > + explicit operator int&&(); > +}; > +struct G2 { > + explicit operator int(); > + operator int&&(); > +}; > +struct H { > + operator int(); > + explicit operator int&(); > +}; > +struct H2 { > + explicit operator int(); > + operator int&(); > +}; > + > +struct Base { }; > +struct Der : Base { }; > + > +template > +struct morph { > + mutable T val{}; > + operator RT() const { return static_cast(val); } > +}; > + > +template using id = T; > + > +// Built-in types. > +SA(!__reference_converts_from_temporary(int, int)); > +SA(!__reference_converts_from_temporary(int&, void)); > +SA(!__reference_converts_from_temporary(int&, const void)); > +SA(!__reference_converts_from_temporary(int&, volatile void)); > +SA(!__reference_converts_from_temporary(int&, const volatile void)); > +SA(!__reference_converts_from_temporary(void, void)); > +SA(!__reference_converts_from_temporary(void, int)); > +SA(!__reference_converts_from_temporary(int&, int)); > +SA(!__reference_converts_from_temporary(int&, int&)); > +SA(!__reference_converts_from_temporary(int&, int&&)); > +SA(!__reference_converts_from_temporary(int&, long)); > +// non-const lvalue reference to type 'int' cannot bind to a value of > unrelated type 'long' > +SA(!__reference_converts_from_temporary(int&, long&)); > +SA(!__reference_converts_from_temporary(int&, long&&)); > +SA( __reference_converts_from_temporary(const int&, int)); > +SA(!__reference_converts_from_temporary(const int&, int&)); > +SA(!__reference_converts_from_temporary(const int&, const int&)); > +SA(!__reference_converts_from_temporary(const int&, int&&)); > +SA( __reference_converts_from_temporary(const int&, long)); > +SA( __reference_converts_from_temporary(const int&, long&)); > +SA( __reference_converts_from_temporary(const int&, long&&)); > +SA( __reference_converts_from_temporary(int&&, int)); > +SA(!__reference_converts_from_temporary(int&&, int&)); > +SA(!__reference_converts_from_temporary(int&&, int&&)); > +SA( __reference_converts_from_temporary(int&&, long)); > +SA( __reference_converts_from_temporary(int&&, long&)); > +SA( __reference_converts_from_temporary(int&&, long&&)); > +SA(!__reference_converts_from_temporary(unsigned int&, double)); > +SA(!__reference_converts_from_temporary(volatile int&, int)); > +SA(!__reference_converts_from_temporary(const volatile int&, int)); > +SA(!__reference_converts_from_temporary(volatile int&, int&)); > +SA(!__reference_converts_from_temporary(const volatile int&, int&)); > +SA(!__reference_converts_from_temporary(volatile int&, int&&)); > +SA(!__reference_converts_from_temporary(const volatile int&, int&&)); > + > +// Classes. > +SA(!__reference_converts_from_temporary(A, A)); > +// A& r(A{}); doesn't construct. > +SA(!__reference_converts_from_temporary(A&, A)); > +SA(!__reference_converts_from_temporary(A&, A&)); > +SA(!__reference_converts_from_temporary(A&, A&&)); > +// Here we get const struct A & r = (const struct A &) &D.2414; > +SA( __reference_converts_from_temporary(const A&, A)); > +SA(!__reference_converts_from_temporary(const A&, A&)); > +SA(!__reference_converts_from_temporary(const A&, const A&)); > +SA(!__reference_converts_from_temporary(const A&, A&&)); > +// Here we get struct A & r = (struct A &) &D.2439; > +SA( __reference_converts_from_temporary(A&&, A)); > +SA(!__reference_converts_from_temporary(A&&, A&)); > +SA(!__reference_converts_from_temporary(A&&, const A&)); > +SA(!__reference_converts_from_temporary(A&&, A&&)); > + > +SA(!__reference_converts_from_temporary(A, B)); > +SA(!__reference_converts_from_temporary(A&, B)); > +SA(!__reference_converts_from_temporary(A&, B&)); > +SA(!__reference_converts_from_temporary(A&, const B&)); > +SA(!__reference_converts_from_temporary(A&, B&&)); > +SA( __reference_converts_from_temporary(const A&, B)); > +SA( __reference_converts_from_temporary(const A&, B&)); > +// Doesn't construct, so it's false. > +SA(!__reference_converts_from_temporary(const A&, const B&)); > +SA( __reference_converts_from_temporary(const A&, B&&)); > +SA( __reference_converts_from_temporary(A&&, B)); > +SA( __reference_converts_from_temporary(A&&, B&)); > +SA(!__reference_converts_from_temporary(A&&, const B&)); > +SA( __reference_converts_from_temporary(A&&, B&&)); > + > +SA(!__reference_converts_from_temporary(const A&, C)); > +SA(!__reference_converts_from_temporary(const A&, C&)); > + > +// Doesn't construct, so it's false. > +SA(!__reference_converts_from_temporary(int&, morph)); > +// Here we get > +// const int & r2 = D.2580 = morph::operator int > +// (&TARGET_EXPR ); (const int &) &D.2580; > +SA( __reference_converts_from_temporary(const int&, morph)); > +SA(!__reference_converts_from_temporary(int&, morph)); > +SA(!__reference_converts_from_temporary(int&, morph)); > +SA(!__reference_converts_from_temporary(int&, morph)); > +SA( __reference_converts_from_temporary(const int&, morph)); > + > +// These are like const C& c(cref); so the explicit ctor C isn't a problem > +// even in copy-init context. const C& r = {}; would be a different > story. > +SA(!__reference_converts_from_temporary(C, C)); > +SA(!__reference_converts_from_temporary(C&, C)); > +SA(!__reference_converts_from_temporary(C&, C&)); > +SA(!__reference_converts_from_temporary(C&, C&&)); > +SA( __reference_converts_from_temporary(const C&, C)); > +SA(!__reference_converts_from_temporary(const C&, C&)); > +SA(!__reference_converts_from_temporary(const C&, const C&)); > +SA(!__reference_converts_from_temporary(const C&, C&&)); > +SA( __reference_converts_from_temporary(C&&, C)); > +SA(!__reference_converts_from_temporary(C&&, C&)); > +SA(!__reference_converts_from_temporary(C&&, const C&)); > +SA(!__reference_converts_from_temporary(C&&, C&&)); > + > +// These are all false ultimately because of CWG 2267, which we implement. > +SA(!__reference_converts_from_temporary(A, D)); > +SA(!__reference_converts_from_temporary(A&, D)); > +SA(!__reference_converts_from_temporary(A&, D&)); > +SA(!__reference_converts_from_temporary(A&, const D&)); > +SA(!__reference_converts_from_temporary(A&, D&&)); > +SA(!__reference_converts_from_temporary(const A&, D)); > +SA(!__reference_converts_from_temporary(const A&, D&)); > +SA(!__reference_converts_from_temporary(const A&, const D&)); > +SA(!__reference_converts_from_temporary(const A&, D&&)); > +SA(!__reference_converts_from_temporary(A&&, D)); > +SA(!__reference_converts_from_temporary(A&&, D&)); > +SA(!__reference_converts_from_temporary(A&&, const D&)); > +SA(!__reference_converts_from_temporary(A&&, D&&)); > + > +SA(!__reference_converts_from_temporary(A, E)); > +/* A& a1(E{}); compiles, but A& a2 = E{}; doesn't. > + With the former, we get A& a = E::operator A& (&TARGET_EXPR {}>) > + so we're not binding the reference to a temporary, although there is > + a temporary involved. So the result is false in both copy- and direct- > + init, albeit for different reasons! */ > +SA(!__reference_converts_from_temporary(A&, E)); > +// A& a = E::operator A& ((struct E *) r)); copy-init doesn't compile. > +SA(!__reference_converts_from_temporary(A&, E&)); > +SA(!__reference_converts_from_temporary(A&, const E&)); > +SA(!__reference_converts_from_temporary(A&, E&&)); > +// direct-init: > +// const A& a = (const struct A &) E::operator A& (&TARGET_EXPR {}>) > +SA(!__reference_converts_from_temporary(const A&, E)); > +SA(!__reference_converts_from_temporary(const A&, E&)); > +SA(!__reference_converts_from_temporary(const A&, const E&)); > +SA(!__reference_converts_from_temporary(const A&, E&&)); > +SA(!__reference_converts_from_temporary(A&&, E)); > +SA(!__reference_converts_from_temporary(A&&, E&)); > +SA(!__reference_converts_from_temporary(A&&, const E&)); > +SA(!__reference_converts_from_temporary(A&&, E&&)); > + > +SA(!__reference_converts_from_temporary(A, F)); > +// A& a1(F{}); and A& a2 = F{}; both invalid. > +SA(!__reference_converts_from_temporary(A&, F)); > +SA(!__reference_converts_from_temporary(A&, F&)); > +SA(!__reference_converts_from_temporary(A&, const F&)); > +SA(!__reference_converts_from_temporary(A&, F&&)); > +SA(!__reference_converts_from_temporary(const A&, F)); > +SA(!__reference_converts_from_temporary(const A&, F&)); > +SA(!__reference_converts_from_temporary(const A&, const F&)); > +SA(!__reference_converts_from_temporary(const A&, F&&)); > +SA(!__reference_converts_from_temporary(A&&, F)); > +SA(!__reference_converts_from_temporary(A&&, F&)); > +SA(!__reference_converts_from_temporary(A&&, const F&)); > +SA(!__reference_converts_from_temporary(A&&, F&&)); > + > +/* This is where _converts_ and _constructs_ will differ: > + in direct-init we use G::operator int&& (no temporary), > + but in copy-init we use G::operator int, where a temporary is created > + to be bound to int&&. */ > +SA( __reference_converts_from_temporary(int&&, G)); > +// Similar to the previous one. > +SA( __reference_converts_from_temporary(const int&, H)); > +/* And here I've switched the explicit-ness. In both copy- and > direct-init > + we call operator int&, so no temporary. */ > +SA(!__reference_converts_from_temporary(int&&, G2)); > +SA(!__reference_converts_from_temporary(const int&, H2)); > + > +SA(!__reference_converts_from_temporary(const Base&, Der)); > + > +// This fails because std::is_constructible_v> is false. > +SA(!__reference_converts_from_temporary(int&&, id)); > + > +// Arrays. > +SA(!__reference_converts_from_temporary(int, int[])); > +SA(!__reference_converts_from_temporary(int[], int[])); > +SA(!__reference_converts_from_temporary(int&, int[])); > +SA(!__reference_converts_from_temporary(int&&, int[])); > +SA(!__reference_converts_from_temporary(const int&, int[])); > diff --git a/libstdc++-v3/include/std/type_traits > b/libstdc++-v3/include/std/type_traits > index e5f58bc2e3f..b1a1deecf66 100644 > --- a/libstdc++-v3/include/std/type_traits > +++ b/libstdc++-v3/include/std/type_traits > @@ -3505,6 +3505,45 @@ template _Args> > template > inline constexpr bool is_scoped_enum_v = is_scoped_enum<_Tp>::value; > > +#define __cpp_lib_reference_from_temporary 202202L > + > + /// True if _Tp is a reference type, a _Up value can be bound to _Tp in > + /// direct-initialization, and a temporary object would be bound to > + /// the reference, false otherwise. > + /// @since C++23 > + template > + struct reference_constructs_from_temporary > + : public bool_constant<__reference_constructs_from_temporary(_Tp, > _Up)> > + { > + > static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}) > + && > std::__is_complete_or_unbounded(__type_identity<_Up>{}), > + "template argument must be a complete class or an unbounded > array"); > + }; > + > + /// True if _Tp is a reference type, a _Up value can be bound to _Tp in > + /// copy-initialization, and a temporary object would be bound to > + /// the reference, false otherwise. > + /// @since C++23 > + template > + struct reference_converts_from_temporary > + : public bool_constant<__reference_converts_from_temporary(_Tp, _Up)> > + { > + > static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}) > + && > std::__is_complete_or_unbounded(__type_identity<_Up>{}), > + "template argument must be a complete class or an unbounded > array"); > + }; > + > + /// @ingroup variable_templates > + /// @since C++23 > + template > + inline constexpr bool reference_constructs_from_temporary_v > + = reference_constructs_from_temporary<_Tp, _Up>::value; > + > + /// @ingroup variable_templates > + /// @since C++23 > + template > + inline constexpr bool reference_converts_from_temporary_v > + = reference_converts_from_temporary<_Tp, _Up>::value; > #endif // C++23 > > #if _GLIBCXX_HAVE_IS_CONSTANT_EVALUATED > diff --git a/libstdc++-v3/include/std/version > b/libstdc++-v3/include/std/version > index 22280e1a349..5edca2f3007 100644 > --- a/libstdc++-v3/include/std/version > +++ b/libstdc++-v3/include/std/version > @@ -300,10 +300,11 @@ > #endif > > #if __cplusplus > 202002L > -// c++2b > +// c++23 > #define __cpp_lib_byteswap 202110L > #define __cpp_lib_constexpr_typeinfo 202106L > #define __cpp_lib_is_scoped_enum 202011L > +#define __cpp_lib_reference_from_temporary 202202L > > #if _GLIBCXX_HOSTED > #define __cpp_lib_adaptor_iterator_pair_constructor 202106L > @@ -335,7 +336,7 @@ > #define __cpp_lib_to_underlying 202102L > #define __cpp_lib_unreachable 202202L > #endif > -#endif // C++2b > +#endif // C++23 > #endif // C++20 > #endif // C++17 > #endif // C++14 > diff --git > a/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc > b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc > new file mode 100644 > index 00000000000..2f62e54d46d > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc > @@ -0,0 +1,110 @@ > +// Copyright (C) 2022 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// . > + > +// { dg-options "-std=gnu++23" } > +// { dg-do compile { target c++23 } } > + > +#include > +#include > + > +#ifndef __cpp_lib_reference_from_temporary > +# error "Feature test macro for reference_from_temporary is missing in > " > +#elif __cpp_lib_reference_from_temporary < 202202L > +# error "Feature test macro for reference_from_temporary has wrong value > in " > +#endif > + > +void test01() > +{ > + using std::reference_constructs_from_temporary; > + using std::reference_converts_from_temporary; > + using namespace __gnu_test; > + > + struct A { A(); }; > + > + struct B { > + operator int(); > + explicit operator int&&(); > + }; > + > + struct C { > + operator int(); > + explicit operator int&(); > + }; > + > + static_assert(test_property int>(false), ""); > + static_assert(test_property void>(false), ""); > + static_assert(test_property const volatile void>(false), ""); > + static_assert(test_property void>(false), ""); > + static_assert(test_property int>(false), ""); > + static_assert(test_property int&>(false), ""); > + static_assert(test_property int&&>(false), ""); > + static_assert(test_property long>(false), ""); > + static_assert(test_property long&>(false), ""); > + static_assert(test_property long&&>(false), ""); > + static_assert(test_property int&, int>(true), ""); > + static_assert(test_property int&, int&>(false), ""); > + static_assert(test_property int&, int&&>(false), ""); > + static_assert(test_property int&, long>(true), ""); > + static_assert(test_property int&, long&>(true), ""); > + static_assert(test_property int&, long&&>(true), ""); > + static_assert(test_property int>(true), ""); > + static_assert(test_property int&>(false), ""); > + static_assert(test_property int&&>(false), ""); > + static_assert(test_property long>(true), ""); > + static_assert(test_property long&>(true), ""); > + static_assert(test_property long&&>(true), ""); > + static_assert(test_property A&, A>(true), ""); > + static_assert(test_property A&, A&&>(false), ""); > + static_assert(test_property A>(true), ""); > + static_assert(test_property int[]>(false), ""); > + static_assert(test_property int&, int[]>(false), ""); > + static_assert(test_property int[]>(false), ""); > + > + static_assert(test_property int>(false), ""); > + static_assert(test_property void>(false), ""); > + static_assert(test_property const volatile void>(false), ""); > + static_assert(test_property void>(false), ""); > + static_assert(test_property int>(false), ""); > + static_assert(test_property int&>(false), ""); > + static_assert(test_property int&&>(false), ""); > + static_assert(test_property long>(false), ""); > + static_assert(test_property long&>(false), ""); > + static_assert(test_property long&&>(false), ""); > + static_assert(test_property int&, int>(true), ""); > + static_assert(test_property int&, int&>(false), ""); > + static_assert(test_property int&, int&&>(false), ""); > + static_assert(test_property int&, long>(true), ""); > + static_assert(test_property int&, long&>(true), ""); > + static_assert(test_property int&, long&&>(true), ""); > + static_assert(test_property int>(true), ""); > + static_assert(test_property int&>(false), ""); > + static_assert(test_property int&&>(false), ""); > + static_assert(test_property long>(true), ""); > + static_assert(test_property long&>(true), ""); > + static_assert(test_property long&&>(true), ""); > + static_assert(test_property A&, A>(true), ""); > + static_assert(test_property A&, A&&>(false), ""); > + static_assert(test_property A>(true), ""); > + static_assert(test_property int[]>(false), ""); > + static_assert(test_property int&, int[]>(false), ""); > + static_assert(test_property int[]>(false), ""); > + > + static_assert(test_property B>(false), ""); > + static_assert(test_property int&, C>(false), ""); > + static_assert(test_property B>(true), ""); > + static_assert(test_property int&, C>(true), ""); > +} > diff --git > a/libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc > b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc > new file mode 100644 > index 00000000000..65770754299 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc > @@ -0,0 +1,28 @@ > +// Copyright (C) 2022 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// . > + > +// { dg-options "-std=gnu++23" } > +// { dg-do compile { target c++23 } } > + > +#include > +#include > + > +void test01() > +{ > + static_assert(std::reference_converts_from_temporary_v std::string&, const char*>); > + static_assert(std::reference_constructs_from_temporary_v std::string&, const char*>); > +} > diff --git > a/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc > b/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc > new file mode 100644 > index 00000000000..f56e7c0dabc > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc > @@ -0,0 +1,27 @@ > +// Copyright (C) 2022 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// . > + > +// { dg-options "-std=gnu++23" } > +// { dg-do compile { target c++23 } } > + > +#include > + > +#ifndef __cpp_lib_reference_from_temporary > +# error "Feature test macro for reference_from_temporary is missing in > " > +#elif __cpp_lib_reference_from_temporary < 202202L > +# error "Feature test macro for reference_from_temporary has wrong value > in " > +#endif > diff --git > a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc > b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc > index 9b3957f7d47..2b03ad7067d 100644 > --- a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc > +++ b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc > @@ -346,3 +346,17 @@ static_assert(disjunction_v true_type>, ""); > static_assert(!disjunction_v false_type>, ""); > +#if __cpp_lib_reference_from_temporary >= 202202L > +static_assert(std::reference_converts_from_temporary_v > + && std::reference_converts_from_temporary_v > + && !std::reference_converts_from_temporary_v > + && !std::reference_converts_from_temporary_v int&&> > + && std::reference_converts_from_temporary_v > + && std::reference_converts_from_temporary_v, > ""); > +static_assert(std::reference_constructs_from_temporary_v > + && std::reference_constructs_from_temporary_v int> > + && !std::reference_constructs_from_temporary_v > + && !std::reference_constructs_from_temporary_v int&&> > + && std::reference_constructs_from_temporary_v > + && std::reference_constructs_from_temporary_v, > ""); > +#endif > > base-commit: 23dd41c480fa9f06c33c1e6090bbae53869f85af > -- > 2.36.1 > >