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 738CD385695A for ; Tue, 12 Jul 2022 20:10:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 738CD385695A Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-302-NDx-LBFOPeKeOUvwQu0gvg-1; Tue, 12 Jul 2022 16:10:13 -0400 X-MC-Unique: NDx-LBFOPeKeOUvwQu0gvg-1 Received: by mail-qk1-f199.google.com with SMTP id bi1-20020a05620a318100b006b572361789so8635806qkb.10 for ; Tue, 12 Jul 2022 13:10:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:references:from:in-reply-to :content-transfer-encoding; bh=m/b5JR5BAU7fzq+Zuh0t+qajDGHFPA/8riJxf8uBIIk=; b=tArZ87rMN0guope5XVrQzcc5La/9fz+mGzpjaqRszSZPmhW4UBbMJJ6Al7MkOqwtLU pmCmvCCosBPs3E2W77zrF3yErolhUIC4SQ+ATfxpvtEHmZEdqPW6nuufHRzHpZS3kt3v 0LuvUt7XxM7bw6v3pyj31rL5hl8o3n9UP70yKx2B4DXYxDTCDxbVU0Am5mFEp1IkvXC3 gvm19sXvGUqfGkuMeemdYQbDjHJWXknv7pCwSiAVQ5lwYFyxq3HvtgTjHV9KmdiDl2ia GAddO0iTjxX50X9resp17BpUpOvv0yLfuMDLHS4PBZI6+kQZMVyHLICp5ZFV2jaeHw/E h4DQ== X-Gm-Message-State: AJIora+w8zAvot5uTkcU80r0RWplKd/OMXhCNMn/9VFN547slmBHAQ13 dLHFMyfaKOY5H27p92Yl1xJhZfUVLLvqnxEX7vydnuZxdhIy45YeCpcPn3BBMP3W0hZ5WpyQvR5 vj3O0U37J1RjnwIs= X-Received: by 2002:a05:622a:213:b0:31e:c569:220e with SMTP id b19-20020a05622a021300b0031ec569220emr1053584qtx.436.1657656612012; Tue, 12 Jul 2022 13:10:12 -0700 (PDT) X-Google-Smtp-Source: AGRyM1tV4HzTZiQuRY1NBf0BuIFXEpj/efcikKGqyh4z7vMJipUlBqWyS+iPj65qMH0VUc/Wk0IjHQ== X-Received: by 2002:a05:622a:213:b0:31e:c569:220e with SMTP id b19-20020a05622a021300b0031ec569220emr1053530qtx.436.1657656611293; Tue, 12 Jul 2022 13:10:11 -0700 (PDT) Received: from [192.168.1.191] ([98.11.146.95]) by smtp.gmail.com with ESMTPSA id x18-20020a05620a449200b006a981a2c483sm10787335qkp.39.2022.07.12.13.10.09 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 12 Jul 2022 13:10:10 -0700 (PDT) Message-ID: <7955e63f-c20e-482c-cea3-db0942ed0149@redhat.com> Date: Tue, 12 Jul 2022 16:10:08 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.11.0 Subject: Re: [PATCH] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] To: Marek Polacek , GCC Patches , libstdc++ , Jonathan Wakely References: <20220708174133.2462418-1-polacek@redhat.com> From: Jason Merrill In-Reply-To: <20220708174133.2462418-1-polacek@redhat.com> 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.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, NICE_REPLY_A, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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: Tue, 12 Jul 2022 20:10:25 -0000 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 **, > @@ -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. > auto_diagnostic_group d; > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > index 2344b5eea00..396a9f6b44e 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 reference_from_temporary (type1, type2, /*direct_init=*/true); > + > + case CPTK_REF_CONVERTS_FROM_TEMPORARY: > + return reference_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)); > + > +// 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 2572d8edd69..bbeb049e1f9 100644 > --- a/libstdc++-v3/include/std/type_traits > +++ b/libstdc++-v3/include/std/type_traits > @@ -3508,6 +3508,45 @@ template > 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(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + > + static_assert(test_property(false), ""); > + static_assert(test_property(false), ""); > + static_assert(test_property(true), ""); > + static_assert(test_property(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); > + static_assert(std::reference_constructs_from_temporary_v); > +} > 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 > + && 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 > + && !std::reference_constructs_from_temporary_v > + && !std::reference_constructs_from_temporary_v > + && std::reference_constructs_from_temporary_v > + && std::reference_constructs_from_temporary_v, ""); > +#endif > > base-commit: 55bb77b50a5ec520f28978ac3fdac1983200e1f7