diff options
author | Marek Polacek <polacek@redhat.com> | 2022-06-29 19:00:54 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2022-07-15 11:30:38 -0400 |
commit | 9a15d3beace26d68561cb3481b70b0bbcb122ca5 (patch) | |
tree | 7db269b8e6e93169326693418b3ac42c50c0d62d /gcc/cp/method.cc | |
parent | 0a8edfbd37d399d1103d86e134ba0a92f8c873c3 (diff) | |
download | gcc-9a15d3beace26d68561cb3481b70b0bbcb122ca5.zip gcc-9a15d3beace26d68561cb3481b70b0bbcb122ca5.tar.gz gcc-9a15d3beace26d68561cb3481b70b0bbcb122ca5.tar.bz2 |
c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477]
This patch implements C++23 P2255R2, which adds two new type traits to
detect reference binding to a temporary. They can be used to detect code
like
std::tuple<const std::string&> t("meow");
which is incorrect because it always creates a dangling reference, because
the std::string temporary is created inside the selected constructor of
std::tuple, and not outside it.
There are two new compiler builtins, __reference_constructs_from_temporary
and __reference_converts_from_temporary. The former is used to simulate
direct- and the latter copy-initialization context. But I had a hard time
finding a test where there's actually a difference. Under DR 2267, both
of these are invalid:
struct A { } a;
struct B { explicit B(const A&); };
const B &b1{a};
const B &b2(a);
so I had to peruse [over.match.ref], and eventually realized that the
difference can be seen here:
struct G {
operator int(); // #1
explicit operator int&&(); // #2
};
int&& r1(G{}); // use #2 (no temporary)
int&& r2 = G{}; // use #1 (a temporary is created to be bound to int&&)
The implementation itself was rather straightforward because we already
have the conv_binds_ref_to_prvalue function. The main function here is
ref_xes_from_temporary.
I've changed the return type of ref_conv_binds_directly to tristate, because
previously the function didn't distinguish between an invalid conversion and
one that binds to a prvalue. Since it no longer returns a bool, I removed
the _p suffix.
The patch also adds the relevant class and variable templates to <type_traits>.
PR c++/104477
gcc/c-family/ChangeLog:
* c-common.cc (c_common_reswords): Add
__reference_constructs_from_temporary and
__reference_converts_from_temporary.
* c-common.h (enum rid): Add RID_REF_CONSTRUCTS_FROM_TEMPORARY and
RID_REF_CONVERTS_FROM_TEMPORARY.
gcc/cp/ChangeLog:
* call.cc (ref_conv_binds_directly_p): Rename to ...
(ref_conv_binds_directly): ... this. Add a new bool parameter. Change
the return type to tristate.
* constraint.cc (diagnose_trait_expr): Handle
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY.
* cp-tree.h: Include "tristate.h".
(enum cp_trait_kind): Add CPTK_REF_CONSTRUCTS_FROM_TEMPORARY
and CPTK_REF_CONVERTS_FROM_TEMPORARY.
(ref_conv_binds_directly_p): Rename to ...
(ref_conv_binds_directly): ... this.
(ref_xes_from_temporary): Declare.
* cxx-pretty-print.cc (pp_cxx_trait_expression): Handle
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY.
* method.cc (ref_xes_from_temporary): New.
* parser.cc (cp_parser_primary_expression): Handle
RID_REF_CONSTRUCTS_FROM_TEMPORARY and RID_REF_CONVERTS_FROM_TEMPORARY.
(cp_parser_trait_expr): Likewise.
(warn_for_range_copy): Adjust to call ref_conv_binds_directly.
* semantics.cc (trait_expr_value): Handle
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY.
(finish_trait_expr): Likewise.
libstdc++-v3/ChangeLog:
* include/std/type_traits (reference_constructs_from_temporary,
reference_converts_from_temporary): New class templates.
(reference_constructs_from_temporary_v,
reference_converts_from_temporary_v): New variable templates.
(__cpp_lib_reference_from_temporary): Define for C++23.
* include/std/version (__cpp_lib_reference_from_temporary): Define for
C++23.
* testsuite/20_util/variable_templates_for_traits.cc: Test
reference_constructs_from_temporary_v and
reference_converts_from_temporary_v.
* testsuite/20_util/reference_from_temporary/value.cc: New test.
* testsuite/20_util/reference_from_temporary/value2.cc: New test.
* testsuite/20_util/reference_from_temporary/version.cc: New test.
gcc/testsuite/ChangeLog:
* g++.dg/ext/reference_constructs_from_temporary1.C: New test.
* g++.dg/ext/reference_converts_from_temporary1.C: New test.
Diffstat (limited to 'gcc/cp/method.cc')
-rw-r--r-- | gcc/cp/method.cc | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index 0dffd64..f2050f6 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -2211,6 +2211,31 @@ is_xible (enum tree_code code, tree to, tree from) return !!expr; } +/* Return true iff conjunction_v<is_reference<T>, is_constructible<T, U>> is + true, and the initialization + T t(VAL<U>); // DIRECT_INIT_P + or + T t = VAL<U>; // !DIRECT_INIT_P + binds t to a temporary object whose lifetime is extended. + VAL<T> is defined in [meta.unary.prop]: + -- If T is a reference or function type, VAL<T> is an expression with the + same type and value category as declval<T>(). + -- Otherwise, VAL<T> is a prvalue that initially has type T. */ + +bool +ref_xes_from_temporary (tree to, tree from, bool direct_init_p) +{ + /* Check is_reference<T>. */ + if (!TYPE_REF_P (to)) + return false; + /* We don't check is_constructible<T, U>: if T isn't constructible + from U, we won't be able to create a conversion. */ + tree val = build_stub_object (from); + if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE) + val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) : rvalue (val); + return ref_conv_binds_directly (to, val, direct_init_p).is_false (); +} + /* Categorize various special_function_kinds. */ #define SFK_CTOR_P(sfk) \ ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor) |