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 | |
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')
-rw-r--r-- | gcc/cp/call.cc | 20 | ||||
-rw-r--r-- | gcc/cp/constraint.cc | 8 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 8 | ||||
-rw-r--r-- | gcc/cp/cxx-pretty-print.cc | 6 | ||||
-rw-r--r-- | gcc/cp/method.cc | 25 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 36 | ||||
-rw-r--r-- | gcc/cp/semantics.cc | 8 |
7 files changed, 89 insertions, 22 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index fc98552..191c68c 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 f2137eb..568318f 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 bec98aa..cf51c39 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "tm.h" #include "hard-reg-set.h" #include "function.h" +#include "tristate.h" /* In order for the format checking to accept the C++ front end diagnostic framework extensions, you must include this file before @@ -1397,7 +1398,9 @@ enum cp_trait_kind CPTK_IS_ASSIGNABLE, CPTK_IS_CONSTRUCTIBLE, CPTK_IS_NOTHROW_ASSIGNABLE, - CPTK_IS_NOTHROW_CONSTRUCTIBLE + CPTK_IS_NOTHROW_CONSTRUCTIBLE, + CPTK_REF_CONSTRUCTS_FROM_TEMPORARY, + CPTK_REF_CONVERTS_FROM_TEMPORARY }; /* The types that we are processing. */ @@ -6520,7 +6523,7 @@ extern bool sufficient_parms_p (const_tree); extern tree type_decays_to (tree); extern tree extract_call_expr (tree); extern tree build_trivial_dtor_call (tree, bool = false); -extern bool ref_conv_binds_directly_p (tree, tree); +extern tristate ref_conv_binds_directly (tree, tree, bool = false); extern tree build_user_type_conversion (tree, tree, int, tsubst_flags_t); extern tree build_new_function_call (tree, vec<tree, va_gc> **, @@ -7105,6 +7108,7 @@ extern tree forward_parm (tree); extern bool is_trivially_xible (enum tree_code, tree, tree); extern bool is_nothrow_xible (enum tree_code, tree, tree); extern bool is_xible (enum tree_code, tree, tree); +extern bool ref_xes_from_temporary (tree, tree, bool); extern tree get_defaulted_eh_spec (tree, tsubst_flags_t = tf_warning_or_error); extern bool maybe_explain_implicit_delete (tree); extern void explain_implicit_non_constexpr (tree); diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index 7e4db2e..4459083 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 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) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index bf9ea36..4f67441 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 2344b5e..96037c2 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; |