From: Ville Voutilainen <ville.voutilainen@gmail.com>
To: Marek Polacek <polacek@redhat.com>
Cc: Jason Merrill <jason@redhat.com>,
Jonathan Wakely <jwakely@redhat.com>,
"libstdc++" <libstdc++@gcc.gnu.org>,
GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477]
Date: Fri, 15 Jul 2022 22:59:41 +0300 [thread overview]
Message-ID: <CAFk2RUYY-=BvpO2oidoQoHV=190iEiNq1EA0+k2Z-B9DDXsUvw@mail.gmail.com> (raw)
In-Reply-To: <YtGIldeWfS+DRQ9j@redhat.com>
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<const std::string&> 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
> > > > > > <type_traits>.
> > > > > >
> > > > > > 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<tree,
> 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_reference<T>,
> > > > > > is_constructible<T, U>> is
> > > > > > + true, and the initialization
> > > > > > + T t(VAL<U>); // DIRECT_INIT_P
> > > > > > + or
> > > > > > + T t = VAL<U>; // !DIRECT_INIT_P
> > > > > > + binds t to a temporary object whose lifetime is extended.
> > > > > > + VAL<T> is defined in [meta.unary.prop]:
> > > > > > + -- If T is a reference or function type, VAL<T> is an
> expression
> > > > > > with the
> > > > > > + same type and value category as declval<T>().
> > > > > > + -- Otherwise, VAL<T> is a prvalue that initially has type
> T. */
> > > > > > +
> > > > > > +bool
> > > > > > +reference_from_temporary (tree to, tree from, bool
> direct_init_p)
> > > > > > +{
> > > > > > + /* Check is_reference<T>. */
> > > > > > + if (!TYPE_REF_P (to))
> > > > > > + return false;
> > > > > > + /* Check is_constructible<T, U>.
> > > > > > + ??? 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<const std::string&> 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
> <type_traits>.
>
> 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<tree, va_gc> **,
> @@ -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_reference<T>, is_constructible<T, U>>
> is
> + true, and the initialization
> + T t(VAL<U>); // DIRECT_INIT_P
> + or
> + T t = VAL<U>; // !DIRECT_INIT_P
> + binds t to a temporary object whose lifetime is extended.
> + VAL<T> is defined in [meta.unary.prop]:
> + -- If T is a reference or function type, VAL<T> is an expression with
> the
> + same type and value category as declval<T>().
> + -- Otherwise, VAL<T> is a prvalue that initially has type T. */
> +
> +bool
> +ref_xes_from_temporary (tree to, tree from, bool direct_init_p)
> +{
> + /* Check is_reference<T>. */
> + if (!TYPE_REF_P (to))
> + return false;
> + /* We don't check is_constructible<T, U>: 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<typename T, typename RT>
> +struct morph {
> + mutable T val{};
> + operator RT() const { return static_cast<RT>(val); }
> +};
> +
> +template<typename T> 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<int, int>));
> +// Here we get
> +// const int & r2 = D.2580 = morph<int, int>::operator int
> +// (&TARGET_EXPR <D.2578, {.val=0}>); (const int &) &D.2580;
> +SA( __reference_constructs_from_temporary(const int&, morph<int, int>));
> +SA(!__reference_constructs_from_temporary(int&, morph<int, int&>));
> +SA(!__reference_constructs_from_temporary(int&, morph<int, const int&>));
> +SA(!__reference_constructs_from_temporary(int&, morph<int, int&&>));
> +SA( __reference_constructs_from_temporary(const int&, morph<long,
> 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 <D.2715,
> {}>)
> + 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 <D.2720,
> {}>)
> +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<int&&, id<int[3]>> is false.
> +SA(!__reference_constructs_from_temporary(int&&, id<int[3]>));
> +
> +// 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<typename T, typename RT>
> +struct morph {
> + mutable T val{};
> + operator RT() const { return static_cast<RT>(val); }
> +};
> +
> +template<typename T> 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<int, int>));
> +// Here we get
> +// const int & r2 = D.2580 = morph<int, int>::operator int
> +// (&TARGET_EXPR <D.2578, {.val=0}>); (const int &) &D.2580;
> +SA( __reference_converts_from_temporary(const int&, morph<int, int>));
> +SA(!__reference_converts_from_temporary(int&, morph<int, int&>));
> +SA(!__reference_converts_from_temporary(int&, morph<int, const int&>));
> +SA(!__reference_converts_from_temporary(int&, morph<int, int&&>));
> +SA( __reference_converts_from_temporary(const int&, morph<long, 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_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 <D.2715,
> {}>)
> + 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 <D.2720,
> {}>)
> +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<int&&, id<int[3]>> is false.
> +SA(!__reference_converts_from_temporary(int&&, id<int[3]>));
> +
> +// 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<typename _Ret, typename _Fn, typename...
> _Args>
> template<typename _Tp>
> 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<typename _Tp, typename _Up>
> + 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<typename _Tp, typename _Up>
> + 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<typename _Tp, typename _Up>
> + inline constexpr bool reference_constructs_from_temporary_v
> + = reference_constructs_from_temporary<_Tp, _Up>::value;
> +
> + /// @ingroup variable_templates
> + /// @since C++23
> + template<typename _Tp, typename _Up>
> + 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
> +// <http://www.gnu.org/licenses/>.
> +
> +// { dg-options "-std=gnu++23" }
> +// { dg-do compile { target c++23 } }
> +
> +#include <type_traits>
> +#include <testsuite_tr1.h>
> +
> +#ifndef __cpp_lib_reference_from_temporary
> +# error "Feature test macro for reference_from_temporary is missing in
> <version>"
> +#elif __cpp_lib_reference_from_temporary < 202202L
> +# error "Feature test macro for reference_from_temporary has wrong value
> in <version>"
> +#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<reference_constructs_from_temporary, int,
> int>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> void>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> const volatile void>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, void,
> void>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> int>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> int&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> int&&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> long>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> long&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> long&&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, int>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, int&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, int&&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, long>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, long&>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, long&&>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> int>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> int&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> int&&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> long>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> long&>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> long&&>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> A&, A>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> A&, A&&>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, A&&,
> A>(true), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&,
> int[]>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, int[]>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> int[]>(false), "");
> +
> + static_assert(test_property<reference_converts_from_temporary, int,
> int>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> void>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> const volatile void>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, void,
> void>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> int>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> int&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> int&&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> long>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> long&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> long&&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, int>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, int&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, int&&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, long>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, long&>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, long&&>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> int>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> int&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> int&&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> long>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> long&>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> long&&>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> A&, A>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> A&, A&&>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, A&&,
> A>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, int&,
> int[]>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> int&, int[]>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> int[]>(false), "");
> +
> + static_assert(test_property<reference_constructs_from_temporary, int&&,
> B>(false), "");
> + static_assert(test_property<reference_constructs_from_temporary, const
> int&, C>(false), "");
> + static_assert(test_property<reference_converts_from_temporary, int&&,
> B>(true), "");
> + static_assert(test_property<reference_converts_from_temporary, const
> 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
> +// <http://www.gnu.org/licenses/>.
> +
> +// { dg-options "-std=gnu++23" }
> +// { dg-do compile { target c++23 } }
> +
> +#include <type_traits>
> +#include <string>
> +
> +void test01()
> +{
> + static_assert(std::reference_converts_from_temporary_v<const
> std::string&, const char*>);
> + static_assert(std::reference_constructs_from_temporary_v<const
> 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
> +// <http://www.gnu.org/licenses/>.
> +
> +// { dg-options "-std=gnu++23" }
> +// { dg-do compile { target c++23 } }
> +
> +#include <version>
> +
> +#ifndef __cpp_lib_reference_from_temporary
> +# error "Feature test macro for reference_from_temporary is missing in
> <version>"
> +#elif __cpp_lib_reference_from_temporary < 202202L
> +# error "Feature test macro for reference_from_temporary has wrong value
> in <version>"
> +#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<false_type, false_type,
> true_type>, "");
> static_assert(!disjunction_v<false_type, false_type,
> false_type>, "");
> +#if __cpp_lib_reference_from_temporary >= 202202L
> +static_assert(std::reference_converts_from_temporary_v<int&&, int>
> + && std::reference_converts_from_temporary_v<const int&, int>
> + && !std::reference_converts_from_temporary_v<int&&, int&&>
> + && !std::reference_converts_from_temporary_v<const int&,
> int&&>
> + && std::reference_converts_from_temporary_v<int&&, long&&>
> + && std::reference_converts_from_temporary_v<int&&, long>,
> "");
> +static_assert(std::reference_constructs_from_temporary_v<int&&, int>
> + && std::reference_constructs_from_temporary_v<const int&,
> int>
> + && !std::reference_constructs_from_temporary_v<int&&, int&&>
> + && !std::reference_constructs_from_temporary_v<const int&,
> int&&>
> + && std::reference_constructs_from_temporary_v<int&&, long&&>
> + && std::reference_constructs_from_temporary_v<int&&, long>,
> "");
> +#endif
>
> base-commit: 23dd41c480fa9f06c33c1e6090bbae53869f85af
> --
> 2.36.1
>
>
next prev parent reply other threads:[~2022-07-15 19:59 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-08 17:41 Marek Polacek
2022-07-11 10:19 ` Jonathan Wakely
2022-07-12 17:16 ` [PATCH v2] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] Marek Polacek
2022-07-12 20:10 ` [PATCH] " Jason Merrill
2022-07-12 20:15 ` Jason Merrill
2022-07-14 17:43 ` Marek Polacek
2022-07-15 3:48 ` Jason Merrill
2022-07-15 15:32 ` Marek Polacek
2022-07-15 19:59 ` Ville Voutilainen [this message]
2022-07-15 20:25 ` Marek Polacek
2022-07-17 21:12 ` Stephan Bergmann
2022-07-19 10:08 ` [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] Jonathan Wakely
2022-07-19 11:10 ` Jonathan Wakely
2022-07-19 14:14 ` Jonathan Wakely
2022-07-14 17:41 ` [PATCH v3] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] Marek Polacek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CAFk2RUYY-=BvpO2oidoQoHV=190iEiNq1EA0+k2Z-B9DDXsUvw@mail.gmail.com' \
--to=ville.voutilainen@gmail.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
--cc=jwakely@redhat.com \
--cc=libstdc++@gcc.gnu.org \
--cc=polacek@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).