From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id A94273857BA1 for ; Tue, 12 Jul 2022 20:15:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org A94273857BA1 Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-661-p7DjEcnwPqSUcB5O3KLZRw-1; Tue, 12 Jul 2022 16:15:04 -0400 X-MC-Unique: p7DjEcnwPqSUcB5O3KLZRw-1 Received: by mail-qv1-f70.google.com with SMTP id p11-20020a0cfd8b000000b0047318870bdfso2422465qvr.14 for ; Tue, 12 Jul 2022 13:15:04 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:from:to:references:in-reply-to :content-transfer-encoding; bh=+TEF3THe7azUguIyUrpvnmtSxUxCueq0kFKaNKU1g8Y=; b=UWyivUHjxPQpjQR1WNtxPmjk/jd9GG+hhvQtvVEDIz0G5JRg68twUmGHmskHneVoUD jjSjh9Pu/FMf2S5OBDJ9B48RIyrDLHgfQ8fQ5Tknb/aE8HHRCL08cFmAKEbCiVmIEsY8 MgLJ1ozSg5RDbOZXvlKbvsl5JtkSCu3UCbJcTZjpX66LX5EXA5BTzWCiDagoUj+/adyb uoCwnMxLCZH88KRPXJqn9VvRAMXP8GSiKONh6KZYPAcGIK1HBIq/TbyUnfESB9SARKuP 57fsNLr7B9eRMjS1MElqI8LKdgA/gvg3iwaNp74wmHtUc8DjOnZJ3cIm7QQLxzHlCIOV Jkqg== X-Gm-Message-State: AJIora+hlSPvDXrLbzetECvXWYTwIrmGjNIsgGkpllOHWybg8Xyv4+ms 2WkvOiCzKKDuRqBnzVs8bV6dYSpAo1QNdFzVIHfvpu9scMpM5YZYa5VlAcccMmUnVhYJ4/EJxco epuIJs19buX7+s1M= X-Received: by 2002:a05:620a:13f9:b0:6b5:b0a7:bce7 with SMTP id h25-20020a05620a13f900b006b5b0a7bce7mr9866qkl.250.1657656903080; Tue, 12 Jul 2022 13:15:03 -0700 (PDT) X-Google-Smtp-Source: AGRyM1v8ESx1UT3myXSW7J81kdPY+MvubTd4Yddq7QgKxjkVLV2P+0r7Uw2tsk6agLj11kvKR6CvCQ== X-Received: by 2002:a05:620a:13f9:b0:6b5:b0a7:bce7 with SMTP id h25-20020a05620a13f900b006b5b0a7bce7mr9820qkl.250.1657656902390; Tue, 12 Jul 2022 13:15:02 -0700 (PDT) Received: from [192.168.1.191] ([98.11.146.95]) by smtp.gmail.com with ESMTPSA id x13-20020a05620a258d00b006a8b6848556sm10045549qko.7.2022.07.12.13.15.01 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 12 Jul 2022 13:15:01 -0700 (PDT) Message-ID: <16983f2a-f56a-1462-c2db-3e4bc6fa6ad7@redhat.com> Date: Tue, 12 Jul 2022 16:15:00 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.11.0 Subject: Re: [PATCH] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] From: Jason Merrill To: Marek Polacek , GCC Patches , libstdc++ , Jonathan Wakely References: <20220708174133.2462418-1-polacek@redhat.com> <7955e63f-c20e-482c-cea3-db0942ed0149@redhat.com> In-Reply-To: <7955e63f-c20e-482c-cea3-db0942ed0149@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, BODY_8BITS, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, NICE_REPLY_A, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 12 Jul 2022 20:15:11 -0000 On 7/12/22 16:10, Jason Merrill wrote: > On 7/8/22 13:41, Marek Polacek wrote: >> This patch implements C++23 P2255R2, which adds two new type traits to >> detect reference binding to a temporary.  They can be used to detect code >> like >> >>    std::tuple t("meow"); >> >> which is incorrect because it always creates a dangling reference, >> because >> the std::string temporary is created inside the selected constructor of >> std::tuple, and not outside it. >> >> There are two new compiler builtins, >> __reference_constructs_from_temporary >> and __reference_converts_from_temporary.  The former is used to simulate >> direct- and the latter copy-initialization context.  But I had a hard >> time >> finding a test where there's actually a difference.  Under DR 2267, both >> of these are invalid: >> >>    struct A { } a; >>    struct B { explicit B(const A&); }; >>    const B &b1{a}; >>    const B &b2(a); >> >> so I had to peruse [over.match.ref], and eventually realized that the >> difference can be seen here: >> >>    struct G { >>      operator int(); // #1 >>      explicit operator int&&(); // #2 >>    }; >> >> int&& r1(G{}); // use #2 (no temporary) >> int&& r2 = G{}; // use #1 (a temporary is created to be bound to int&&) >> >> The implementation itself was rather straightforward because we already >> have conv_binds_ref_to_prvalue.  The main function here is >> reference_from_temporary.  The renaming to ref_conv_binds_to_temporary_p >> is because previously the function didn't distinguish between an invalid >> conversion and one that binds to a prvalue. >> >> The patch also adds the relevant class and variable templates to >> . >> >> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? >> >>     PR c++/104477 >> >> gcc/c-family/ChangeLog: >> >>     * c-common.cc (c_common_reswords): Add >>     __reference_constructs_from_temporary and >>     __reference_converts_from_temporary. >>     * c-common.h (enum rid): Add RID_REF_CONSTRUCTS_FROM_TEMPORARY and >>     RID_REF_CONVERTS_FROM_TEMPORARY. >> >> gcc/cp/ChangeLog: >> >>     * call.cc (ref_conv_binds_directly_p): Rename to ... >>     (ref_conv_binds_to_temporary_p): ... this.  Add a new bool >>     parameter.  Return true only if the conversion is valid and >>     conv_binds_ref_to_prvalue returns true. >>     * constraint.cc (diagnose_trait_expr): Handle >>     CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and >> CPTK_REF_CONVERTS_FROM_TEMPORARY. >>     * cp-tree.h (enum cp_trait_kind): Add >> CPTK_REF_CONSTRUCTS_FROM_TEMPORARY >>     and CPTK_REF_CONVERTS_FROM_TEMPORARY. >>     (ref_conv_binds_directly_p): Rename to ... >>     (ref_conv_binds_to_temporary_p): ... this. >>     (reference_from_temporary): Declare. >>     * cxx-pretty-print.cc (pp_cxx_trait_expression): Handle >>     CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and >> CPTK_REF_CONVERTS_FROM_TEMPORARY. >>     * method.cc (reference_from_temporary): New. >>     * parser.cc (cp_parser_primary_expression): Handle >>     RID_REF_CONSTRUCTS_FROM_TEMPORARY and >> RID_REF_CONVERTS_FROM_TEMPORARY. >>     (cp_parser_trait_expr): Likewise. >>     (warn_for_range_copy): Adjust to call ref_conv_binds_to_temporary_p. >>     * semantics.cc (trait_expr_value): Handle >>     CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and >> CPTK_REF_CONVERTS_FROM_TEMPORARY. >>     (finish_trait_expr): Likewise. >> >> libstdc++-v3/ChangeLog: >> >>     * include/std/type_traits (reference_constructs_from_temporary, >>     reference_converts_from_temporary): New class templates. >>     (reference_constructs_from_temporary_v, >>     reference_converts_from_temporary_v): New variable templates. >>     (__cpp_lib_reference_from_temporary): Define for C++23. >>     * include/std/version (__cpp_lib_reference_from_temporary): Define >> for >>     C++23. >>     * testsuite/20_util/variable_templates_for_traits.cc: Test >>     reference_constructs_from_temporary_v and >>     reference_converts_from_temporary_v. >>     * testsuite/20_util/reference_from_temporary/value.cc: New test. >>     * testsuite/20_util/reference_from_temporary/value2.cc: New test. >>     * testsuite/20_util/reference_from_temporary/version.cc: New test. >> >> gcc/testsuite/ChangeLog: >> >>     * g++.dg/ext/reference_constructs_from_temporary1.C: New test. >>     * g++.dg/ext/reference_converts_from_temporary1.C: New test. >> --- >>   gcc/c-family/c-common.cc                      |   4 + >>   gcc/c-family/c-common.h                       |   2 + >>   gcc/cp/call.cc                                |  14 +- >>   gcc/cp/constraint.cc                          |   8 + >>   gcc/cp/cp-tree.h                              |   7 +- >>   gcc/cp/cxx-pretty-print.cc                    |   6 + >>   gcc/cp/method.cc                              |  28 +++ >>   gcc/cp/parser.cc                              |  14 +- >>   gcc/cp/semantics.cc                           |   8 + >>   .../reference_constructs_from_temporary1.C    | 214 ++++++++++++++++++ >>   .../ext/reference_converts_from_temporary1.C  | 214 ++++++++++++++++++ >>   libstdc++-v3/include/std/type_traits          |  39 ++++ >>   libstdc++-v3/include/std/version              |   5 +- >>   .../20_util/reference_from_temporary/value.cc | 110 +++++++++ >>   .../reference_from_temporary/value2.cc        |  28 +++ >>   .../reference_from_temporary/version.cc       |  27 +++ >>   .../20_util/variable_templates_for_traits.cc  |  14 ++ >>   17 files changed, 730 insertions(+), 12 deletions(-) >>   create mode 100644 >> gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C >>   create mode 100644 >> gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C >>   create mode 100644 >> libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc >>   create mode 100644 >> libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc >>   create mode 100644 >> libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc >> >> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc >> index 1b8e73f7bc5..655c3aefee6 100644 >> --- a/gcc/c-family/c-common.cc >> +++ b/gcc/c-family/c-common.cc >> @@ -537,6 +537,10 @@ const struct c_common_resword c_common_reswords[] = >>     { "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY }, >>     { "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY }, >>     { "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, >> D_CXXONLY }, >> +  { "__reference_constructs_from_temporary", >> RID_REF_CONSTRUCTS_FROM_TEMPORARY, >> +                    D_CXXONLY }, >> +  { "__reference_converts_from_temporary", >> RID_REF_CONVERTS_FROM_TEMPORARY, >> +                    D_CXXONLY }, >>     /* C++ transactional memory.  */ >>     { "synchronized",    RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM }, >> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h >> index c0900848965..f9064393b4e 100644 >> --- a/gcc/c-family/c-common.h >> +++ b/gcc/c-family/c-common.h >> @@ -184,6 +184,8 @@ enum rid >>     RID_IS_UNION,                RID_UNDERLYING_TYPE, >>     RID_IS_ASSIGNABLE,           RID_IS_CONSTRUCTIBLE, >>     RID_IS_NOTHROW_ASSIGNABLE,   RID_IS_NOTHROW_CONSTRUCTIBLE, >> +  RID_REF_CONSTRUCTS_FROM_TEMPORARY, >> +  RID_REF_CONVERTS_FROM_TEMPORARY, >>     /* C++11 */ >>     RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, >> RID_STATIC_ASSERT, >> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc >> index fc98552fda2..1ba209f61f1 100644 >> --- a/gcc/cp/call.cc >> +++ b/gcc/cp/call.cc >> @@ -9109,21 +9109,23 @@ conv_binds_ref_to_prvalue (conversion *c) >>     return conv_is_prvalue (next_conversion (c)); >>   } >> -/* True iff converting EXPR to a reference type TYPE does not involve >> -   creating a temporary.  */ >> +/* True iff converting EXPR to a reference type TYPE binds the >> reference to >> +   a temporary.  DIRECT_INIT_P says whether the conversion should be >> done >> +   in direct- or copy-initialization context.  */ >>   bool >> -ref_conv_binds_directly_p (tree type, tree expr) >> +ref_conv_binds_to_temporary_p (tree type, tree expr, >> +                   bool direct_init_p /*= false*/) >>   { >>     gcc_assert (TYPE_REF_P (type)); >>     /* Get the high-water mark for the CONVERSION_OBSTACK.  */ >>     void *p = conversion_obstack_alloc (0); >> +  const int flags = direct_init_p ? LOOKUP_NORMAL : LOOKUP_IMPLICIT; >>     conversion *conv = implicit_conversion (type, TREE_TYPE (expr), expr, >> -                      /*c_cast_p=*/false, >> -                      LOOKUP_IMPLICIT, tf_none); >> -  bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue (conv); >> +                      /*c_cast_p=*/false, flags, tf_none); >> +  bool ret = conv && !conv->bad_p && conv_binds_ref_to_prvalue (conv); >>     /* Free all the conversions we allocated.  */ >>     obstack_free (&conversion_obstack, p); >> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc >> index 591155cee22..648cc9d176d 100644 >> --- a/gcc/cp/constraint.cc >> +++ b/gcc/cp/constraint.cc >> @@ -3687,6 +3687,14 @@ diagnose_trait_expr (tree expr, tree args) >>       case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: >>         inform (loc, "  %qT does not have unique object >> representations", t1); >>         break; >> +    case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: >> +      inform (loc, "  %qT is not a reference that binds to a temporary " >> +          "object of type %qT (direct-initialization)", t1, t2); >> +      break; >> +    case CPTK_REF_CONVERTS_FROM_TEMPORARY: >> +      inform (loc, "  %qT is not a reference that binds to a temporary " >> +          "object of type %qT (copy-initialization)", t1, t2); >> +      break; >>       case CPTK_BASES: >>       case CPTK_DIRECT_BASES: >>       case CPTK_UNDERLYING_TYPE: >> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h >> index 2fde4f83b41..c3bed31e455 100644 >> --- a/gcc/cp/cp-tree.h >> +++ b/gcc/cp/cp-tree.h >> @@ -1397,7 +1397,9 @@ enum cp_trait_kind >>     CPTK_IS_ASSIGNABLE, >>     CPTK_IS_CONSTRUCTIBLE, >>     CPTK_IS_NOTHROW_ASSIGNABLE, >> -  CPTK_IS_NOTHROW_CONSTRUCTIBLE >> +  CPTK_IS_NOTHROW_CONSTRUCTIBLE, >> +  CPTK_REF_CONSTRUCTS_FROM_TEMPORARY, >> +  CPTK_REF_CONVERTS_FROM_TEMPORARY >>   }; >>   /* The types that we are processing.  */ >> @@ -6520,7 +6522,7 @@ extern bool sufficient_parms_p >> (const_tree); >>   extern tree type_decays_to            (tree); >>   extern tree extract_call_expr            (tree); >>   extern tree build_trivial_dtor_call        (tree, bool = false); >> -extern bool ref_conv_binds_directly_p        (tree, tree); >> +extern bool ref_conv_binds_to_temporary_p    (tree, tree, bool = false); >>   extern tree build_user_type_conversion        (tree, tree, int, >>                            tsubst_flags_t); >>   extern tree build_new_function_call        (tree, vec **, >> @@ -7105,6 +7107,7 @@ extern tree forward_parm            (tree); >>   extern bool is_trivially_xible            (enum tree_code, tree, tree); >>   extern bool is_nothrow_xible            (enum tree_code, tree, tree); >>   extern bool is_xible                (enum tree_code, tree, tree); >> +extern bool reference_from_temporary        (tree, tree, bool); >>   extern tree get_defaulted_eh_spec        (tree, tsubst_flags_t = >> tf_warning_or_error); >>   extern bool maybe_explain_implicit_delete    (tree); >>   extern void explain_implicit_non_constexpr    (tree); >> diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc >> index 7e4db2e413b..44590830a61 100644 >> --- a/gcc/cp/cxx-pretty-print.cc >> +++ b/gcc/cp/cxx-pretty-print.cc >> @@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer >> *pp, tree t) >>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE: >>         pp_cxx_ws_string (pp, "__is_nothrow_constructible"); >>         break; >> +    case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: >> +      pp_cxx_ws_string (pp, "__reference_constructs_from_temporary"); >> +      break; >> +    case CPTK_REF_CONVERTS_FROM_TEMPORARY: >> +      pp_cxx_ws_string (pp, "__reference_converts_from_temporary"); >> +      break; >>       default: >>         gcc_unreachable (); >> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc >> index 0dffd648b0b..dd9715b6725 100644 >> --- a/gcc/cp/method.cc >> +++ b/gcc/cp/method.cc >> @@ -2211,6 +2211,34 @@ is_xible (enum tree_code code, tree to, tree from) >>     return !!expr; >>   } >> +/* Return true iff conjunction_v, is_constructible> U>> is >> +   true, and the initialization >> +     T t(VAL); // DIRECT_INIT_P >> +   or >> +     T t = VAL; // !DIRECT_INIT_P >> +   binds t to a temporary object whose lifetime is extended. >> +   VAL is defined in [meta.unary.prop]: >> +   -- If T is a reference or function type, VAL is an expression >> with the >> +   same type and value category as declval(). >> +   -- Otherwise, VAL is a prvalue that initially has type T.   */ >> + >> +bool >> +reference_from_temporary (tree to, tree from, bool direct_init_p) >> +{ >> +  /* Check is_reference.  */ >> +  if (!TYPE_REF_P (to)) >> +    return false; >> +  /* Check is_constructible. >> +     ??? This check doesn't seem to be necessary; if T isn't >> constructible >> +     from U, we won't be able to create a conversion.  */ >> +  if (!is_xible (INIT_EXPR, to, build_tree_list (NULL_TREE, from))) >> +    return false; > > I agree with the comment, did you try leaving this out?  If it stays I'd > think it needs to consider direct_init_p. > >> +  tree val = build_stub_object (from); >> +  if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE) >> +    val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) : rvalue >> (val); >> +  return ref_conv_binds_to_temporary_p (to, val, direct_init_p); >> +} >> + >>   /* Categorize various special_function_kinds.  */ >>   #define SFK_CTOR_P(sfk) \ >>     ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor) >> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc >> index bf9ea3685f8..edee94bda13 100644 >> --- a/gcc/cp/parser.cc >> +++ b/gcc/cp/parser.cc >> @@ -5917,6 +5917,8 @@ cp_parser_primary_expression (cp_parser *parser, >>       case RID_IS_CONSTRUCTIBLE: >>       case RID_IS_NOTHROW_ASSIGNABLE: >>       case RID_IS_NOTHROW_CONSTRUCTIBLE: >> +    case RID_REF_CONSTRUCTS_FROM_TEMPORARY: >> +    case RID_REF_CONVERTS_FROM_TEMPORARY: >>         return cp_parser_trait_expr (parser, token->keyword); >>       // C++ concepts >> @@ -10988,6 +10990,14 @@ cp_parser_trait_expr (cp_parser* parser, enum >> rid keyword) >>         kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE; >>         variadic = true; >>         break; >> +    case RID_REF_CONSTRUCTS_FROM_TEMPORARY: >> +      kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY; >> +      binary = true; >> +      break; >> +    case RID_REF_CONVERTS_FROM_TEMPORARY: >> +      kind = CPTK_REF_CONVERTS_FROM_TEMPORARY; >> +      binary = true; >> +      break; >>       default: >>         gcc_unreachable (); >>       } >> @@ -13811,7 +13821,7 @@ warn_for_range_copy (tree decl, tree expr) >>     if (TYPE_REF_P (type)) >>       { >> -      if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, expr)) >> +      if (glvalue_p (expr) && ref_conv_binds_to_temporary_p (type, >> expr)) >>       { >>         auto_diagnostic_group d; >>         if (warning_at (loc, OPT_Wrange_loop_construct, >> @@ -13842,7 +13852,7 @@ warn_for_range_copy (tree decl, tree expr) >>     tree rtype = cp_build_reference_type (type, /*rval*/false); >>     /* If we could initialize the reference directly, it wouldn't >> involve any >>        copies.  */ >> -  if (!ref_conv_binds_directly_p (rtype, expr)) >> +  if (ref_conv_binds_to_temporary_p (rtype, expr)) >>       return; > > I think this case wants the old handling of invalid conversions you > mentioned in your intro; we don't want to suggest changing to a > reference if that's ill-formed. > > In passing we might change the comment to "If we can initialize a > reference directly, suggest that to avoid the copy." and move it above > the rtype declaration. Hmm, and I suspect we get false positives when expr is a prvalue, so initializing a non-reference variable of the same cv-unqualified type involves no extra copy? >>     auto_diagnostic_group d; >> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc >> index 2344b5eea00..396a9f6b44e 100644 >> --- a/gcc/cp/semantics.cc >> +++ b/gcc/cp/semantics.cc >> @@ -12007,6 +12007,12 @@ trait_expr_value (cp_trait_kind kind, tree >> type1, tree type2) >>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE: >>         return is_nothrow_xible (INIT_EXPR, type1, type2); >> +    case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: >> +      return reference_from_temporary (type1, type2, >> /*direct_init=*/true); >> + >> +    case CPTK_REF_CONVERTS_FROM_TEMPORARY: >> +      return reference_from_temporary (type1, type2, >> /*direct_init=*/false); >> + >>       default: >>         gcc_unreachable (); >>         return false; >> @@ -12088,6 +12094,8 @@ finish_trait_expr (location_t loc, >> cp_trait_kind kind, tree type1, tree type2) >>       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE: >>       case CPTK_IS_NOTHROW_ASSIGNABLE: >>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE: >> +    case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: >> +    case CPTK_REF_CONVERTS_FROM_TEMPORARY: >>         if (!check_trait_type (type1) >>         || !check_trait_type (type2)) >>       return error_mark_node; >> diff --git >> a/gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C >> b/gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C >> new file mode 100644 >> index 00000000000..76de905a35d >> --- /dev/null >> +++ b/gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C >> @@ -0,0 +1,214 @@ >> +// P2255R2 >> +// PR c++/104477 >> +// { dg-do compile { target c++11 } } >> + >> +#define SA(X) static_assert((X),#X) >> + >> +struct A { A(); }; >> +struct B { operator A(); }; >> +struct C { explicit C(); }; >> +struct D { explicit operator A(); }; >> +struct E { explicit operator A&(); }; >> +struct F { explicit operator A&&(); }; >> +// Could use a class template with explicit(bool), but then this >> would need >> +// C++20. >> +struct G { >> +  operator int(); >> +  explicit operator int&&(); >> +}; >> +struct G2 { >> +  explicit operator int(); >> +  operator int&&(); >> +}; >> +struct H { >> +  operator int(); >> +  explicit operator int&(); >> +}; >> +struct H2 { >> +  explicit operator int(); >> +  operator int&(); >> +}; >> + >> +struct Base { }; >> +struct Der : Base { }; >> + >> +template >> +struct morph { >> +  mutable T val{}; >> +  operator RT() const { return static_cast(val); } >> +}; >> + >> +template using id = T; >> + >> +// Built-in types. >> +SA(!__reference_constructs_from_temporary(int, int)); >> +SA(!__reference_constructs_from_temporary(int&, void)); >> +SA(!__reference_constructs_from_temporary(int&, const void)); >> +SA(!__reference_constructs_from_temporary(int&, volatile void)); >> +SA(!__reference_constructs_from_temporary(int&, const volatile void)); >> +SA(!__reference_constructs_from_temporary(void, void)); >> +SA(!__reference_constructs_from_temporary(void, int)); >> +SA(!__reference_constructs_from_temporary(int&, int)); >> +SA(!__reference_constructs_from_temporary(int&, int&)); >> +SA(!__reference_constructs_from_temporary(int&, int&&)); >> +SA(!__reference_constructs_from_temporary(int&, long)); >> +// non-const lvalue reference to type 'int' cannot bind to a value of >> unrelated type 'long' >> +SA(!__reference_constructs_from_temporary(int&, long&)); >> +SA(!__reference_constructs_from_temporary(int&, long&&)); >> +SA( __reference_constructs_from_temporary(const int&, int)); >> +SA(!__reference_constructs_from_temporary(const int&, int&)); >> +SA(!__reference_constructs_from_temporary(const int&, const int&)); >> +SA(!__reference_constructs_from_temporary(const int&, int&&)); >> +SA( __reference_constructs_from_temporary(const int&, long)); >> +SA( __reference_constructs_from_temporary(const int&, long&)); >> +SA( __reference_constructs_from_temporary(const int&, long&&)); >> +SA( __reference_constructs_from_temporary(int&&, int)); >> +SA(!__reference_constructs_from_temporary(int&&, int&)); >> +SA(!__reference_constructs_from_temporary(int&&, int&&)); >> +SA( __reference_constructs_from_temporary(int&&, long)); >> +SA( __reference_constructs_from_temporary(int&&, long&)); >> +SA( __reference_constructs_from_temporary(int&&, long&&)); >> +SA(!__reference_constructs_from_temporary(unsigned int&, double)); >> +SA(!__reference_constructs_from_temporary(volatile int&, int)); >> +SA(!__reference_constructs_from_temporary(const volatile int&, int)); >> +SA(!__reference_constructs_from_temporary(volatile int&, int&)); >> +SA(!__reference_constructs_from_temporary(const volatile int&, int&)); >> +SA(!__reference_constructs_from_temporary(volatile int&, int&&)); >> +SA(!__reference_constructs_from_temporary(const volatile int&, int&&)); >> + >> +// Classes. >> +SA(!__reference_constructs_from_temporary(A, A)); >> +// A& r(A{}); doesn't construct. >> +SA(!__reference_constructs_from_temporary(A&, A)); >> +SA(!__reference_constructs_from_temporary(A&, A&)); >> +SA(!__reference_constructs_from_temporary(A&, A&&)); >> +// Here we get const struct A & r = (const struct A &) &D.2414; >> +SA( __reference_constructs_from_temporary(const A&, A)); >> +SA(!__reference_constructs_from_temporary(const A&, A&)); >> +SA(!__reference_constructs_from_temporary(const A&, const A&)); >> +SA(!__reference_constructs_from_temporary(const A&, A&&)); >> +// Here we get struct A & r = (struct A &) &D.2439; >> +SA( __reference_constructs_from_temporary(A&&, A)); >> +SA(!__reference_constructs_from_temporary(A&&, A&)); >> +SA(!__reference_constructs_from_temporary(A&&, const A&)); >> +SA(!__reference_constructs_from_temporary(A&&, A&&)); >> + >> +SA(!__reference_constructs_from_temporary(A, B)); >> +SA(!__reference_constructs_from_temporary(A&, B)); >> +SA(!__reference_constructs_from_temporary(A&, B&)); >> +SA(!__reference_constructs_from_temporary(A&, const B&)); >> +SA(!__reference_constructs_from_temporary(A&, B&&)); >> +SA( __reference_constructs_from_temporary(const A&, B)); >> +SA( __reference_constructs_from_temporary(const A&, B&)); >> +// Doesn't construct, so it's false. >> +SA(!__reference_constructs_from_temporary(const A&, const B&)); >> +SA( __reference_constructs_from_temporary(const A&, B&&)); >> +SA( __reference_constructs_from_temporary(A&&, B)); >> +SA( __reference_constructs_from_temporary(A&&, B&)); >> +SA(!__reference_constructs_from_temporary(A&&, const B&)); >> +SA( __reference_constructs_from_temporary(A&&, B&&)); >> + >> +SA(!__reference_constructs_from_temporary(const A&, C)); >> +SA(!__reference_constructs_from_temporary(const A&, C&)); >> + >> +// Doesn't construct, so it's false. >> +SA(!__reference_constructs_from_temporary(int&, morph)); >> +// Here we get >> +//   const int & r2 = D.2580 = morph::operator int >> +//     (&TARGET_EXPR ); (const int &) &D.2580; >> +SA( __reference_constructs_from_temporary(const int&, morph)); >> +SA(!__reference_constructs_from_temporary(int&, morph)); >> +SA(!__reference_constructs_from_temporary(int&, morph> int&>)); >> +SA(!__reference_constructs_from_temporary(int&, morph)); >> +SA( __reference_constructs_from_temporary(const int&, morph> long&>)); >> + >> +// These are like const C& c(cref); so the explicit ctor C isn't a >> problem >> +// even in copy-init context.  const C& r = {}; would be a different >> story. >> +SA(!__reference_constructs_from_temporary(C, C)); >> +SA(!__reference_constructs_from_temporary(C&, C)); >> +SA(!__reference_constructs_from_temporary(C&, C&)); >> +SA(!__reference_constructs_from_temporary(C&, C&&)); >> +SA( __reference_constructs_from_temporary(const C&, C)); >> +SA(!__reference_constructs_from_temporary(const C&, C&)); >> +SA(!__reference_constructs_from_temporary(const C&, const C&)); >> +SA(!__reference_constructs_from_temporary(const C&, C&&)); >> +SA( __reference_constructs_from_temporary(C&&, C)); >> +SA(!__reference_constructs_from_temporary(C&&, C&)); >> +SA(!__reference_constructs_from_temporary(C&&, const C&)); >> +SA(!__reference_constructs_from_temporary(C&&, C&&)); >> + >> +// These are all false ultimately because of CWG 2267, which we >> implement. >> +SA(!__reference_constructs_from_temporary(A, D)); >> +SA(!__reference_constructs_from_temporary(A&, D)); >> +SA(!__reference_constructs_from_temporary(A&, D&)); >> +SA(!__reference_constructs_from_temporary(A&, const D&)); >> +SA(!__reference_constructs_from_temporary(A&, D&&)); >> +SA(!__reference_constructs_from_temporary(const A&, D)); >> +SA(!__reference_constructs_from_temporary(const A&, D&)); >> +SA(!__reference_constructs_from_temporary(const A&, const D&)); >> +SA(!__reference_constructs_from_temporary(const A&, D&&)); >> +SA(!__reference_constructs_from_temporary(A&&, D)); >> +SA(!__reference_constructs_from_temporary(A&&, D&)); >> +SA(!__reference_constructs_from_temporary(A&&, const D&)); >> +SA(!__reference_constructs_from_temporary(A&&, D&&)); >> + >> +SA(!__reference_constructs_from_temporary(A, E)); >> +/* A& a1(E{}); compiles, but A& a2 = E{}; doesn't. >> +   With the former, we get A& a = E::operator A& (&TARGET_EXPR >> ) >> +   so we're not binding the reference to a temporary, although there is >> +   a temporary involved.  So the result is false in both copy- and >> direct- >> +   init, albeit for different reasons!  */ >> +SA(!__reference_constructs_from_temporary(A&, E)); >> +// A& a = E::operator A& ((struct E *) r)); copy-init doesn't compile. >> +SA(!__reference_constructs_from_temporary(A&, E&)); >> +SA(!__reference_constructs_from_temporary(A&, const E&)); >> +SA(!__reference_constructs_from_temporary(A&, E&&)); >> +// direct-init: >> +// const A& a = (const struct A &) E::operator A& (&TARGET_EXPR >> ) >> +SA(!__reference_constructs_from_temporary(const A&, E)); >> +SA(!__reference_constructs_from_temporary(const A&, E&)); >> +SA(!__reference_constructs_from_temporary(const A&, const E&)); >> +SA(!__reference_constructs_from_temporary(const A&, E&&)); >> +SA(!__reference_constructs_from_temporary(A&&, E)); >> +SA(!__reference_constructs_from_temporary(A&&, E&)); >> +SA(!__reference_constructs_from_temporary(A&&, const E&)); >> +SA(!__reference_constructs_from_temporary(A&&, E&&)); >> + >> +SA(!__reference_constructs_from_temporary(A, F)); >> +// A& a1(F{}); and A& a2 = F{}; both invalid. >> +SA(!__reference_constructs_from_temporary(A&, F)); >> +SA(!__reference_constructs_from_temporary(A&, F&)); >> +SA(!__reference_constructs_from_temporary(A&, const F&)); >> +SA(!__reference_constructs_from_temporary(A&, F&&)); >> +SA(!__reference_constructs_from_temporary(const A&, F)); >> +SA(!__reference_constructs_from_temporary(const A&, F&)); >> +SA(!__reference_constructs_from_temporary(const A&, const F&)); >> +SA(!__reference_constructs_from_temporary(const A&, F&&)); >> +SA(!__reference_constructs_from_temporary(A&&, F)); >> +SA(!__reference_constructs_from_temporary(A&&, F&)); >> +SA(!__reference_constructs_from_temporary(A&&, const F&)); >> +SA(!__reference_constructs_from_temporary(A&&, F&&)); >> + >> +/* This is where _converts_ and _constructs_ will differ: >> +   in direct-init we use G::operator int&& (no temporary), >> +   but in copy-init we use G::operator int, where a temporary is created >> +   to be bound to int&&.  */ >> +SA(!__reference_constructs_from_temporary(int&&, G)); >> +// Similar to the previous one. >> +SA(!__reference_constructs_from_temporary(const int&, H)); >> +/* And here I've switched the explicit-ness.  In both copy- and >> direct-init >> +   we call operator int&, so no temporary.  */ >> +SA(!__reference_constructs_from_temporary(int&&, G2)); >> +SA(!__reference_constructs_from_temporary(const int&, H2)); >> + >> +SA(!__reference_constructs_from_temporary(const Base&, Der)); >> + >> +// This fails because std::is_constructible_v> is >> false. >> +SA(!__reference_constructs_from_temporary(int&&, id)); >> + >> +// Arrays. >> +SA(!__reference_constructs_from_temporary(int, int[])); >> +SA(!__reference_constructs_from_temporary(int[], int[])); >> +SA(!__reference_constructs_from_temporary(int&, int[])); >> +SA(!__reference_constructs_from_temporary(int&&, int[])); >> +SA(!__reference_constructs_from_temporary(const int&, int[])); >> diff --git >> a/gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C >> b/gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C >> new file mode 100644 >> index 00000000000..90196c38742 >> --- /dev/null >> +++ b/gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C >> @@ -0,0 +1,214 @@ >> +// P2255R2 >> +// PR c++/104477 >> +// { dg-do compile { target c++11 } } >> + >> +#define SA(X) static_assert((X),#X) >> + >> +struct A { A(); }; >> +struct B { operator A(); }; >> +struct C { explicit C(); }; >> +struct D { explicit operator A(); }; >> +struct E { explicit operator A&(); }; >> +struct F { explicit operator A&&(); }; >> +// Could use a class template with explicit(bool), but then this >> would need >> +// C++20. >> +struct G { >> +  operator int(); >> +  explicit operator int&&(); >> +}; >> +struct G2 { >> +  explicit operator int(); >> +  operator int&&(); >> +}; >> +struct H { >> +  operator int(); >> +  explicit operator int&(); >> +}; >> +struct H2 { >> +  explicit operator int(); >> +  operator int&(); >> +}; >> + >> +struct Base { }; >> +struct Der : Base { }; >> + >> +template >> +struct morph { >> +  mutable T val{}; >> +  operator RT() const { return static_cast(val); } >> +}; >> + >> +template using id = T; >> + >> +// Built-in types. >> +SA(!__reference_converts_from_temporary(int, int)); >> +SA(!__reference_converts_from_temporary(int&, void)); >> +SA(!__reference_converts_from_temporary(int&, const void)); >> +SA(!__reference_converts_from_temporary(int&, volatile void)); >> +SA(!__reference_converts_from_temporary(int&, const volatile void)); >> +SA(!__reference_converts_from_temporary(void, void)); >> +SA(!__reference_converts_from_temporary(void, int)); >> +SA(!__reference_converts_from_temporary(int&, int)); >> +SA(!__reference_converts_from_temporary(int&, int&)); >> +SA(!__reference_converts_from_temporary(int&, int&&)); >> +SA(!__reference_converts_from_temporary(int&, long)); >> +// non-const lvalue reference to type 'int' cannot bind to a value of >> unrelated type 'long' >> +SA(!__reference_converts_from_temporary(int&, long&)); >> +SA(!__reference_converts_from_temporary(int&, long&&)); >> +SA( __reference_converts_from_temporary(const int&, int)); >> +SA(!__reference_converts_from_temporary(const int&, int&)); >> +SA(!__reference_converts_from_temporary(const int&, const int&)); >> +SA(!__reference_converts_from_temporary(const int&, int&&)); >> +SA( __reference_converts_from_temporary(const int&, long)); >> +SA( __reference_converts_from_temporary(const int&, long&)); >> +SA( __reference_converts_from_temporary(const int&, long&&)); >> +SA( __reference_converts_from_temporary(int&&, int)); >> +SA(!__reference_converts_from_temporary(int&&, int&)); >> +SA(!__reference_converts_from_temporary(int&&, int&&)); >> +SA( __reference_converts_from_temporary(int&&, long)); >> +SA( __reference_converts_from_temporary(int&&, long&)); >> +SA( __reference_converts_from_temporary(int&&, long&&)); >> +SA(!__reference_converts_from_temporary(unsigned int&, double)); >> +SA(!__reference_converts_from_temporary(volatile int&, int)); >> +SA(!__reference_converts_from_temporary(const volatile int&, int)); >> +SA(!__reference_converts_from_temporary(volatile int&, int&)); >> +SA(!__reference_converts_from_temporary(const volatile int&, int&)); >> +SA(!__reference_converts_from_temporary(volatile int&, int&&)); >> +SA(!__reference_converts_from_temporary(const volatile int&, int&&)); >> + >> +// Classes. >> +SA(!__reference_converts_from_temporary(A, A)); >> +// A& r(A{}); doesn't construct. >> +SA(!__reference_converts_from_temporary(A&, A)); >> +SA(!__reference_converts_from_temporary(A&, A&)); >> +SA(!__reference_converts_from_temporary(A&, A&&)); >> +// Here we get const struct A & r = (const struct A &) &D.2414; >> +SA( __reference_converts_from_temporary(const A&, A)); >> +SA(!__reference_converts_from_temporary(const A&, A&)); >> +SA(!__reference_converts_from_temporary(const A&, const A&)); >> +SA(!__reference_converts_from_temporary(const A&, A&&)); >> +// Here we get struct A & r = (struct A &) &D.2439; >> +SA( __reference_converts_from_temporary(A&&, A)); >> +SA(!__reference_converts_from_temporary(A&&, A&)); >> +SA(!__reference_converts_from_temporary(A&&, const A&)); >> +SA(!__reference_converts_from_temporary(A&&, A&&)); >> + >> +SA(!__reference_converts_from_temporary(A, B)); >> +SA(!__reference_converts_from_temporary(A&, B)); >> +SA(!__reference_converts_from_temporary(A&, B&)); >> +SA(!__reference_converts_from_temporary(A&, const B&)); >> +SA(!__reference_converts_from_temporary(A&, B&&)); >> +SA( __reference_converts_from_temporary(const A&, B)); >> +SA( __reference_converts_from_temporary(const A&, B&)); >> +// Doesn't construct, so it's false. >> +SA(!__reference_converts_from_temporary(const A&, const B&)); >> +SA( __reference_converts_from_temporary(const A&, B&&)); >> +SA( __reference_converts_from_temporary(A&&, B)); >> +SA( __reference_converts_from_temporary(A&&, B&)); >> +SA(!__reference_converts_from_temporary(A&&, const B&)); >> +SA( __reference_converts_from_temporary(A&&, B&&)); >> + >> +SA(!__reference_converts_from_temporary(const A&, C)); >> +SA(!__reference_converts_from_temporary(const A&, C&)); >> + >> +// Doesn't construct, so it's false. >> +SA(!__reference_converts_from_temporary(int&, morph)); >> +// Here we get >> +//   const int & r2 = D.2580 = morph::operator int >> +//     (&TARGET_EXPR ); (const int &) &D.2580; >> +SA( __reference_converts_from_temporary(const int&, morph)); >> +SA(!__reference_converts_from_temporary(int&, morph)); >> +SA(!__reference_converts_from_temporary(int&, morph)); >> +SA(!__reference_converts_from_temporary(int&, morph)); >> +SA( __reference_converts_from_temporary(const int&, morph> 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 >> ) >> +   so we're not binding the reference to a temporary, although there is >> +   a temporary involved.  So the result is false in both copy- and >> direct- >> +   init, albeit for different reasons!  */ >> +SA(!__reference_converts_from_temporary(A&, E)); >> +// A& a = E::operator A& ((struct E *) r)); copy-init doesn't compile. >> +SA(!__reference_converts_from_temporary(A&, E&)); >> +SA(!__reference_converts_from_temporary(A&, const E&)); >> +SA(!__reference_converts_from_temporary(A&, E&&)); >> +// direct-init: >> +// const A& a = (const struct A &) E::operator A& (&TARGET_EXPR >> ) >> +SA(!__reference_converts_from_temporary(const A&, E)); >> +SA(!__reference_converts_from_temporary(const A&, E&)); >> +SA(!__reference_converts_from_temporary(const A&, const E&)); >> +SA(!__reference_converts_from_temporary(const A&, E&&)); >> +SA(!__reference_converts_from_temporary(A&&, E)); >> +SA(!__reference_converts_from_temporary(A&&, E&)); >> +SA(!__reference_converts_from_temporary(A&&, const E&)); >> +SA(!__reference_converts_from_temporary(A&&, E&&)); >> + >> +SA(!__reference_converts_from_temporary(A, F)); >> +// A& a1(F{}); and A& a2 = F{}; both invalid. >> +SA(!__reference_converts_from_temporary(A&, F)); >> +SA(!__reference_converts_from_temporary(A&, F&)); >> +SA(!__reference_converts_from_temporary(A&, const F&)); >> +SA(!__reference_converts_from_temporary(A&, F&&)); >> +SA(!__reference_converts_from_temporary(const A&, F)); >> +SA(!__reference_converts_from_temporary(const A&, F&)); >> +SA(!__reference_converts_from_temporary(const A&, const F&)); >> +SA(!__reference_converts_from_temporary(const A&, F&&)); >> +SA(!__reference_converts_from_temporary(A&&, F)); >> +SA(!__reference_converts_from_temporary(A&&, F&)); >> +SA(!__reference_converts_from_temporary(A&&, const F&)); >> +SA(!__reference_converts_from_temporary(A&&, F&&)); >> + >> +/* This is where _converts_ and _constructs_ will differ: >> +   in direct-init we use G::operator int&& (no temporary), >> +   but in copy-init we use G::operator int, where a temporary is created >> +   to be bound to int&&.  */ >> +SA( __reference_converts_from_temporary(int&&, G)); >> +// Similar to the previous one. >> +SA( __reference_converts_from_temporary(const int&, H)); >> +/* And here I've switched the explicit-ness.  In both copy- and >> direct-init >> +   we call operator int&, so no temporary.  */ >> +SA(!__reference_converts_from_temporary(int&&, G2)); >> +SA(!__reference_converts_from_temporary(const int&, H2)); >> + >> +SA(!__reference_converts_from_temporary(const Base&, Der)); >> + >> +// This fails because std::is_constructible_v> is >> false. >> +SA(!__reference_converts_from_temporary(int&&, id)); >> + >> +// Arrays. >> +SA(!__reference_converts_from_temporary(int, int[])); >> +SA(!__reference_converts_from_temporary(int[], int[])); >> +SA(!__reference_converts_from_temporary(int&, int[])); >> +SA(!__reference_converts_from_temporary(int&&, int[])); >> +SA(!__reference_converts_from_temporary(const int&, int[])); >> diff --git a/libstdc++-v3/include/std/type_traits >> b/libstdc++-v3/include/std/type_traits >> index 2572d8edd69..bbeb049e1f9 100644 >> --- a/libstdc++-v3/include/std/type_traits >> +++ b/libstdc++-v3/include/std/type_traits >> @@ -3508,6 +3508,45 @@ template> typename... _Args> >>     template >>       inline constexpr bool is_scoped_enum_v = >> is_scoped_enum<_Tp>::value; >> +#define __cpp_lib_reference_from_temporary 202202L >> + >> +  /// True if _Tp is a reference type, a _Up value can be bound to >> _Tp in >> +  /// direct-initialization, and a temporary object would be bound to >> +  /// the reference, false otherwise. >> +  /// @since C++23 >> +  template >> +    struct reference_constructs_from_temporary >> +    : public >> __bool_constant<__reference_constructs_from_temporary(_Tp, _Up)> >> +    { >> + >> static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}) >> +            && std::__is_complete_or_unbounded(__type_identity<_Up>{}), >> +    "template argument must be a complete class or an unbounded array"); >> +    }; >> + >> +  /// True if _Tp is a reference type, a _Up value can be bound to >> _Tp in >> +  /// copy-initialization, and a temporary object would be bound to >> +  /// the reference, false otherwise. >> +  /// @since C++23 >> +  template >> +    struct reference_converts_from_temporary >> +    : public __bool_constant<__reference_converts_from_temporary(_Tp, >> _Up)> >> +    { >> + >> static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}) >> +            && std::__is_complete_or_unbounded(__type_identity<_Up>{}), >> +    "template argument must be a complete class or an unbounded array"); >> +    }; >> + >> +  /// @ingroup variable_templates >> +  /// @since C++23 >> +  template >> +    inline constexpr bool reference_constructs_from_temporary_v >> +      = reference_constructs_from_temporary<_Tp, _Up>::value; >> + >> +  /// @ingroup variable_templates >> +  /// @since C++23 >> +  template >> +    inline constexpr bool reference_converts_from_temporary_v >> +      = reference_converts_from_temporary<_Tp, _Up>::value; >>   #endif // C++23 >>   #if _GLIBCXX_HAVE_IS_CONSTANT_EVALUATED >> diff --git a/libstdc++-v3/include/std/version >> b/libstdc++-v3/include/std/version >> index 22280e1a349..5edca2f3007 100644 >> --- a/libstdc++-v3/include/std/version >> +++ b/libstdc++-v3/include/std/version >> @@ -300,10 +300,11 @@ >>   #endif >>   #if __cplusplus > 202002L >> -// c++2b >> +// c++23 >>   #define __cpp_lib_byteswap 202110L >>   #define __cpp_lib_constexpr_typeinfo 202106L >>   #define __cpp_lib_is_scoped_enum 202011L >> +#define __cpp_lib_reference_from_temporary 202202L >>   #if _GLIBCXX_HOSTED >>   #define __cpp_lib_adaptor_iterator_pair_constructor 202106L >> @@ -335,7 +336,7 @@ >>   #define __cpp_lib_to_underlying 202102L >>   #define __cpp_lib_unreachable 202202L >>   #endif >> -#endif // C++2b >> +#endif // C++23 >>   #endif // C++20 >>   #endif // C++17 >>   #endif // C++14 >> diff --git >> a/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc >> b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc >> new file mode 100644 >> index 00000000000..2f62e54d46d >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc >> @@ -0,0 +1,110 @@ >> +// Copyright (C) 2022 Free Software Foundation, Inc. >> +// >> +// This file is part of the GNU ISO C++ Library.  This library is free >> +// software; you can redistribute it and/or modify it under the >> +// terms of the GNU General Public License as published by the >> +// Free Software Foundation; either version 3, or (at your option) >> +// any later version. >> + >> +// This library is distributed in the hope that it will be useful, >> +// but WITHOUT ANY WARRANTY; without even the implied warranty of >> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the >> +// GNU General Public License for more details. >> + >> +// You should have received a copy of the GNU General Public License >> along >> +// with this library; see the file COPYING3.  If not see >> +// . >> + >> +// { dg-options "-std=gnu++23" } >> +// { dg-do compile { target c++23 } } >> + >> +#include >> +#include >> + >> +#ifndef __cpp_lib_reference_from_temporary >> +# error "Feature test macro for reference_from_temporary is missing >> in " >> +#elif __cpp_lib_reference_from_temporary < 202202L >> +# error "Feature test macro for reference_from_temporary has wrong >> value in " >> +#endif >> + >> +void test01() >> +{ >> +  using std::reference_constructs_from_temporary; >> +  using std::reference_converts_from_temporary; >> +  using namespace __gnu_test; >> + >> +  struct A { A(); }; >> + >> +  struct B { >> +    operator int(); >> +    explicit operator int&&(); >> +  }; >> + >> +  struct C { >> +    operator int(); >> +    explicit operator int&(); >> +  }; >> + >> +  static_assert(test_property> int, int>(false), ""); >> +  static_assert(test_property> int&, void>(false), ""); >> +  static_assert(test_property> int&, const volatile void>(false), ""); >> +  static_assert(test_property> void, void>(false), ""); >> +  static_assert(test_property> int&, int>(false), ""); >> +  static_assert(test_property> int&, int&>(false), ""); >> +  static_assert(test_property> int&, int&&>(false), ""); >> +  static_assert(test_property> int&, long>(false), ""); >> +  static_assert(test_property> int&, long&>(false), ""); >> +  static_assert(test_property> int&, long&&>(false), ""); >> +  static_assert(test_property> const int&, int>(true), ""); >> +  static_assert(test_property> const int&, int&>(false), ""); >> +  static_assert(test_property> const int&, int&&>(false), ""); >> +  static_assert(test_property> const int&, long>(true), ""); >> +  static_assert(test_property> const int&, long&>(true), ""); >> +  static_assert(test_property> const int&, long&&>(true), ""); >> +  static_assert(test_property> int&&, int>(true), ""); >> +  static_assert(test_property> int&&, int&>(false), ""); >> +  static_assert(test_property> int&&, int&&>(false), ""); >> +  static_assert(test_property> int&&, long>(true), ""); >> +  static_assert(test_property> int&&, long&>(true), ""); >> +  static_assert(test_property> int&&, long&&>(true), ""); >> +  static_assert(test_property> const A&, A>(true), ""); >> +  static_assert(test_property> const A&, A&&>(false), ""); >> +  static_assert(test_property> A&&, A>(true), ""); >> +  static_assert(test_property> int&, int[]>(false), ""); >> +  static_assert(test_property> const int&, int[]>(false), ""); >> +  static_assert(test_property> int&&, int[]>(false), ""); >> + >> +  static_assert(test_property> int>(false), ""); >> +  static_assert(test_property> int&, void>(false), ""); >> +  static_assert(test_property> int&, const volatile void>(false), ""); >> +  static_assert(test_property> void, void>(false), ""); >> +  static_assert(test_property> int&, int>(false), ""); >> +  static_assert(test_property> int&, int&>(false), ""); >> +  static_assert(test_property> int&, int&&>(false), ""); >> +  static_assert(test_property> int&, long>(false), ""); >> +  static_assert(test_property> int&, long&>(false), ""); >> +  static_assert(test_property> int&, long&&>(false), ""); >> +  static_assert(test_property> const int&, int>(true), ""); >> +  static_assert(test_property> const int&, int&>(false), ""); >> +  static_assert(test_property> const int&, int&&>(false), ""); >> +  static_assert(test_property> const int&, long>(true), ""); >> +  static_assert(test_property> const int&, long&>(true), ""); >> +  static_assert(test_property> const int&, long&&>(true), ""); >> +  static_assert(test_property> int&&, int>(true), ""); >> +  static_assert(test_property> int&&, int&>(false), ""); >> +  static_assert(test_property> int&&, int&&>(false), ""); >> +  static_assert(test_property> int&&, long>(true), ""); >> +  static_assert(test_property> int&&, long&>(true), ""); >> +  static_assert(test_property> int&&, long&&>(true), ""); >> +  static_assert(test_property> const A&, A>(true), ""); >> +  static_assert(test_property> const A&, A&&>(false), ""); >> +  static_assert(test_property> A>(true), ""); >> +  static_assert(test_property> int&, int[]>(false), ""); >> +  static_assert(test_property> const int&, int[]>(false), ""); >> +  static_assert(test_property> int&&, int[]>(false), ""); >> + >> +  static_assert(test_property> int&&, B>(false), ""); >> +  static_assert(test_property> const int&, C>(false), ""); >> +  static_assert(test_property> int&&, B>(true), ""); >> +  static_assert(test_property> 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 >> +// . >> + >> +// { dg-options "-std=gnu++23" } >> +// { dg-do compile { target c++23 } } >> + >> +#include >> +#include >> + >> +void test01() >> +{ >> +  static_assert(std::reference_converts_from_temporary_v> std::string&, const char*>); >> +  static_assert(std::reference_constructs_from_temporary_v> std::string&, const char*>); >> +} >> diff --git >> a/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc >> b/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc >> new file mode 100644 >> index 00000000000..f56e7c0dabc >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc >> @@ -0,0 +1,27 @@ >> +// Copyright (C) 2022 Free Software Foundation, Inc. >> +// >> +// This file is part of the GNU ISO C++ Library.  This library is free >> +// software; you can redistribute it and/or modify it under the >> +// terms of the GNU General Public License as published by the >> +// Free Software Foundation; either version 3, or (at your option) >> +// any later version. >> + >> +// This library is distributed in the hope that it will be useful, >> +// but WITHOUT ANY WARRANTY; without even the implied warranty of >> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the >> +// GNU General Public License for more details. >> + >> +// You should have received a copy of the GNU General Public License >> along >> +// with this library; see the file COPYING3.  If not see >> +// . >> + >> +// { dg-options "-std=gnu++23" } >> +// { dg-do compile { target c++23 } } >> + >> +#include >> + >> +#ifndef __cpp_lib_reference_from_temporary >> +# error "Feature test macro for reference_from_temporary is missing >> in " >> +#elif __cpp_lib_reference_from_temporary < 202202L >> +# error "Feature test macro for reference_from_temporary has wrong >> value in " >> +#endif >> diff --git >> a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc >> b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc >> index 9b3957f7d47..2b03ad7067d 100644 >> --- a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc >> +++ b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc >> @@ -346,3 +346,17 @@ static_assert(disjunction_v>                 true_type>, ""); >>   static_assert(!disjunction_v>                 false_type>, ""); >> +#if __cpp_lib_reference_from_temporary >= 202202L >> +static_assert(std::reference_converts_from_temporary_v >> +          && std::reference_converts_from_temporary_v >> +          && !std::reference_converts_from_temporary_v >> +          && !std::reference_converts_from_temporary_v> int&&> >> +          && std::reference_converts_from_temporary_v >> +          && std::reference_converts_from_temporary_v, ""); >> +static_assert(std::reference_constructs_from_temporary_v >> +          && std::reference_constructs_from_temporary_v >> +          && !std::reference_constructs_from_temporary_v >> +          && !std::reference_constructs_from_temporary_v> int&&> >> +          && std::reference_constructs_from_temporary_v >> +          && std::reference_constructs_from_temporary_v, >> ""); >> +#endif >> >> base-commit: 55bb77b50a5ec520f28978ac3fdac1983200e1f7 >