From: Jason Merrill <jason@redhat.com>
To: Marek Polacek <polacek@redhat.com>,
GCC Patches <gcc-patches@gcc.gnu.org>,
libstdc++ <libstdc++@gcc.gnu.org>,
Jonathan Wakely <jwakely@redhat.com>
Subject: Re: [PATCH] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477]
Date: Tue, 12 Jul 2022 16:15:00 -0400 [thread overview]
Message-ID: <16983f2a-f56a-1462-c2db-3e4bc6fa6ad7@redhat.com> (raw)
In-Reply-To: <7955e63f-c20e-482c-cea3-db0942ed0149@redhat.com>
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?
>> 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<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 2572d8edd69..bbeb049e1f9 100644
>> --- a/libstdc++-v3/include/std/type_traits
>> +++ b/libstdc++-v3/include/std/type_traits
>> @@ -3508,6 +3508,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: 55bb77b50a5ec520f28978ac3fdac1983200e1f7
>
next prev parent reply other threads:[~2022-07-12 20:15 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-08 17:41 [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] 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 [this message]
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 ` [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] Ville Voutilainen
2022-07-15 20:25 ` [PATCH] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] 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=16983f2a-f56a-1462-c2db-3e4bc6fa6ad7@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--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).