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