This patch implements C++23 P2255R2, which adds two new type traits to
detect reference binding to a temporary. They can be used to detect code
like
std::tuple<const std::string&> t("meow");
which is incorrect because it always creates a dangling reference,
because
the std::string temporary is created inside the selected constructor of
std::tuple, and not outside it.
There are two new compiler builtins,
__reference_constructs_from_temporary
and __reference_converts_from_temporary. The former is used to simulate
direct- and the latter copy-initialization context. But I had a
hard time
finding a test where there's actually a difference. Under DR 2267, both
of these are invalid:
struct A { } a;
struct B { explicit B(const A&); };
const B &b1{a};
const B &b2(a);
so I had to peruse [over.match.ref], and eventually realized that the
difference can be seen here:
struct G {
operator int(); // #1
explicit operator int&&(); // #2
};
int&& r1(G{}); // use #2 (no temporary)
int&& r2 = G{}; // use #1 (a temporary is created to be bound to int&&)
The implementation itself was rather straightforward because we already
have conv_binds_ref_to_prvalue. The main function here is
reference_from_temporary. The renaming to ref_conv_binds_to_temporary_p
is because previously the function didn't distinguish between an invalid
conversion and one that binds to a prvalue.
The patch also adds the relevant class and variable templates to
<type_traits>.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
PR c++/104477
gcc/c-family/ChangeLog:
* c-common.cc (c_common_reswords): Add
__reference_constructs_from_temporary and
__reference_converts_from_temporary.
* c-common.h (enum rid): Add RID_REF_CONSTRUCTS_FROM_TEMPORARY and
RID_REF_CONVERTS_FROM_TEMPORARY.
gcc/cp/ChangeLog:
* call.cc (ref_conv_binds_directly_p): Rename to ...
(ref_conv_binds_to_temporary_p): ... this. Add a new bool
parameter. Return true only if the conversion is valid and
conv_binds_ref_to_prvalue returns true.
* constraint.cc (diagnose_trait_expr): Handle
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and
CPTK_REF_CONVERTS_FROM_TEMPORARY.
* cp-tree.h (enum cp_trait_kind): Add
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY
and CPTK_REF_CONVERTS_FROM_TEMPORARY.
(ref_conv_binds_directly_p): Rename to ...
(ref_conv_binds_to_temporary_p): ... this.
(reference_from_temporary): Declare.
* cxx-pretty-print.cc (pp_cxx_trait_expression): Handle
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and
CPTK_REF_CONVERTS_FROM_TEMPORARY.
* method.cc (reference_from_temporary): New.
* parser.cc (cp_parser_primary_expression): Handle
RID_REF_CONSTRUCTS_FROM_TEMPORARY and
RID_REF_CONVERTS_FROM_TEMPORARY.
(cp_parser_trait_expr): Likewise.
(warn_for_range_copy): Adjust to call ref_conv_binds_to_temporary_p.
* semantics.cc (trait_expr_value): Handle
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and
CPTK_REF_CONVERTS_FROM_TEMPORARY.
(finish_trait_expr): Likewise.
libstdc++-v3/ChangeLog:
* include/std/type_traits (reference_constructs_from_temporary,
reference_converts_from_temporary): New class templates.
(reference_constructs_from_temporary_v,
reference_converts_from_temporary_v): New variable templates.
(__cpp_lib_reference_from_temporary): Define for C++23.
* include/std/version (__cpp_lib_reference_from_temporary):
Define for
C++23.
* testsuite/20_util/variable_templates_for_traits.cc: Test
reference_constructs_from_temporary_v and
reference_converts_from_temporary_v.
* testsuite/20_util/reference_from_temporary/value.cc: New test.
* testsuite/20_util/reference_from_temporary/value2.cc: New test.
* testsuite/20_util/reference_from_temporary/version.cc: New test.
gcc/testsuite/ChangeLog:
* g++.dg/ext/reference_constructs_from_temporary1.C: New test.
* g++.dg/ext/reference_converts_from_temporary1.C: New test.
---
gcc/c-family/c-common.cc | 4 +
gcc/c-family/c-common.h | 2 +
gcc/cp/call.cc | 14 +-
gcc/cp/constraint.cc | 8 +
gcc/cp/cp-tree.h | 7 +-
gcc/cp/cxx-pretty-print.cc | 6 +
gcc/cp/method.cc | 28 +++
gcc/cp/parser.cc | 14 +-
gcc/cp/semantics.cc | 8 +
.../reference_constructs_from_temporary1.C | 214 ++++++++++++++++++
.../ext/reference_converts_from_temporary1.C | 214 ++++++++++++++++++
libstdc++-v3/include/std/type_traits | 39 ++++
libstdc++-v3/include/std/version | 5 +-
.../20_util/reference_from_temporary/value.cc | 110 +++++++++
.../reference_from_temporary/value2.cc | 28 +++
.../reference_from_temporary/version.cc | 27 +++
.../20_util/variable_templates_for_traits.cc | 14 ++
17 files changed, 730 insertions(+), 12 deletions(-)
create mode 100644
gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C
create mode 100644
gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C
create mode 100644
libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc
create mode 100644
libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc
create mode 100644
libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 1b8e73f7bc5..655c3aefee6 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -537,6 +537,10 @@ const struct c_common_resword c_common_reswords[] =
{ "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY },
{ "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY },
{ "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE,
D_CXXONLY },
+ { "__reference_constructs_from_temporary",
RID_REF_CONSTRUCTS_FROM_TEMPORARY,
+ D_CXXONLY },
+ { "__reference_converts_from_temporary",
RID_REF_CONVERTS_FROM_TEMPORARY,
+ D_CXXONLY },
/* C++ transactional memory. */
{ "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index c0900848965..f9064393b4e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -184,6 +184,8 @@ enum rid
RID_IS_UNION, RID_UNDERLYING_TYPE,
RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE,
RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE,
+ RID_REF_CONSTRUCTS_FROM_TEMPORARY,
+ RID_REF_CONVERTS_FROM_TEMPORARY,
/* C++11 */
RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR,
RID_STATIC_ASSERT,
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index fc98552fda2..1ba209f61f1 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -9109,21 +9109,23 @@ conv_binds_ref_to_prvalue (conversion *c)
return conv_is_prvalue (next_conversion (c));
}
-/* True iff converting EXPR to a reference type TYPE does not involve
- creating a temporary. */
+/* True iff converting EXPR to a reference type TYPE binds the
reference to
+ a temporary. DIRECT_INIT_P says whether the conversion should
be done
+ in direct- or copy-initialization context. */
bool
-ref_conv_binds_directly_p (tree type, tree expr)
+ref_conv_binds_to_temporary_p (tree type, tree expr,
+ bool direct_init_p /*= false*/)
{
gcc_assert (TYPE_REF_P (type));
/* Get the high-water mark for the CONVERSION_OBSTACK. */
void *p = conversion_obstack_alloc (0);
+ const int flags = direct_init_p ? LOOKUP_NORMAL : LOOKUP_IMPLICIT;
conversion *conv = implicit_conversion (type, TREE_TYPE (expr), expr,
- /*c_cast_p=*/false,
- LOOKUP_IMPLICIT, tf_none);
- bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue (conv);
+ /*c_cast_p=*/false, flags, tf_none);
+ bool ret = conv && !conv->bad_p && conv_binds_ref_to_prvalue (conv);
/* Free all the conversions we allocated. */
obstack_free (&conversion_obstack, p);
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 591155cee22..648cc9d176d 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3687,6 +3687,14 @@ diagnose_trait_expr (tree expr, tree args)
case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
inform (loc, " %qT does not have unique object
representations", t1);
break;
+ case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
+ inform (loc, " %qT is not a reference that binds to a temporary "
+ "object of type %qT (direct-initialization)", t1, t2);
+ break;
+ case CPTK_REF_CONVERTS_FROM_TEMPORARY:
+ inform (loc, " %qT is not a reference that binds to a temporary "
+ "object of type %qT (copy-initialization)", t1, t2);
+ break;
case CPTK_BASES:
case CPTK_DIRECT_BASES:
case CPTK_UNDERLYING_TYPE:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 2fde4f83b41..c3bed31e455 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1397,7 +1397,9 @@ enum cp_trait_kind
CPTK_IS_ASSIGNABLE,
CPTK_IS_CONSTRUCTIBLE,
CPTK_IS_NOTHROW_ASSIGNABLE,
- CPTK_IS_NOTHROW_CONSTRUCTIBLE
+ CPTK_IS_NOTHROW_CONSTRUCTIBLE,
+ CPTK_REF_CONSTRUCTS_FROM_TEMPORARY,
+ CPTK_REF_CONVERTS_FROM_TEMPORARY
};
/* The types that we are processing. */
@@ -6520,7 +6522,7 @@ extern bool sufficient_parms_p
(const_tree);
extern tree type_decays_to (tree);
extern tree extract_call_expr (tree);
extern tree build_trivial_dtor_call (tree, bool = false);
-extern bool ref_conv_binds_directly_p (tree, tree);
+extern bool ref_conv_binds_to_temporary_p (tree, tree, bool = false);
extern tree build_user_type_conversion (tree, tree, int,
tsubst_flags_t);
extern tree build_new_function_call (tree, vec<tree, va_gc> **,
@@ -7105,6 +7107,7 @@ extern tree forward_parm (tree);
extern bool is_trivially_xible (enum tree_code, tree, tree);
extern bool is_nothrow_xible (enum tree_code, tree, tree);
extern bool is_xible (enum tree_code, tree, tree);
+extern bool reference_from_temporary (tree, tree, bool);
extern tree get_defaulted_eh_spec (tree, tsubst_flags_t =
tf_warning_or_error);
extern bool maybe_explain_implicit_delete (tree);
extern void explain_implicit_non_constexpr (tree);
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index 7e4db2e413b..44590830a61 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer
*pp, tree t)
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
pp_cxx_ws_string (pp, "__is_nothrow_constructible");
break;
+ case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
+ pp_cxx_ws_string (pp, "__reference_constructs_from_temporary");
+ break;
+ case CPTK_REF_CONVERTS_FROM_TEMPORARY:
+ pp_cxx_ws_string (pp, "__reference_converts_from_temporary");
+ break;
default:
gcc_unreachable ();
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 0dffd648b0b..dd9715b6725 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -2211,6 +2211,34 @@ is_xible (enum tree_code code, tree to, tree from)
return !!expr;
}
+/* Return true iff conjunction_v<is_reference<T>,
is_constructible<T, U>> is
+ true, and the initialization
+ T t(VAL<U>); // DIRECT_INIT_P
+ or
+ T t = VAL<U>; // !DIRECT_INIT_P
+ binds t to a temporary object whose lifetime is extended.
+ VAL<T> is defined in [meta.unary.prop]:
+ -- If T is a reference or function type, VAL<T> is an expression
with the
+ same type and value category as declval<T>().
+ -- Otherwise, VAL<T> is a prvalue that initially has type T. */
+
+bool
+reference_from_temporary (tree to, tree from, bool direct_init_p)
+{
+ /* Check is_reference<T>. */
+ if (!TYPE_REF_P (to))
+ return false;
+ /* Check is_constructible<T, U>.
+ ??? This check doesn't seem to be necessary; if T isn't
constructible
+ from U, we won't be able to create a conversion. */
+ if (!is_xible (INIT_EXPR, to, build_tree_list (NULL_TREE, from)))
+ return false;
+ 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;